C# 特性详解

Posted dsw846

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 特性详解相关的知识,希望对你有一定的参考价值。

  特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集、类型、方法、属性等)相关联。这些元数据是在编译过程中创建,并嵌入到程序集中。特性与程序实体关联后,即可在运行时使用名为“反射”的技术查询特性。反射是个普通术语,它描述了在运行过程中检查和处理程序元素的功能。

特性具有以下属性:

  • 特性可向程序中添加元数据。元数据是有关在程序中定义的类型的信息。所有的 .NET 程序集都包含指定的一组元数据,这些元数据描述在程序集中定义的类型和类型成员。可以添加自定义特性,以指定所需的任何附加信息。

  • 可以将一个或多个特性应用到整个程序集、模块或较小的程序元素(如类和属性)。

  • 特性可以与方法和属性相同的方式接受参数。

  • 程序可以使用反射检查自己的元数据或其他程序内的元数据。

元数据是什么?

  你注意过程序及编译的时候的pdb文件了吗?pdb文件里面存储了,关于程序集内部的所有的成员信息,例如,成员的数据类型,属性类型,方法返回值,方法入参类型,就是程序及内部所有的定义信息的数据信息,是存储定义信息的一类数据信息,程序集里面的所有的关于声明类的数据信息,包括方法间调用,都是存储在元数据里面。

详细查看:http://www.cnblogs.com/DswCnblog/p/5344119.html

 

特性可以放置在几乎所有的声明中。在 C# 中,特性的指定方法为:将括在方括号中的特性名置于其应用到的实体的声明上方,一个元素上面可以放置多个特性

[System.Serializable]
public class SampleClass
{
    // Objects of this type can be serialized.
}

  根据约定,所有特性名称都以单词“Attribute”结束,以便将它们与“.NET Framework”中的其他项区分。在代码中使用特性,C#编译器会把字符串Attribute追加到这个特性名称后面,然后在其搜索路径的所有名称空间中搜索指定名称的类。如果该特性的名称已字符串Attribute结尾,编译器就不会把字符串加到组合名称中。

预定义特性(Attribute)

.Net 框架提供了三种预定义特性:

  • AttributeUsage
  • Conditional
  • Obsolete

AttributeUsage特性

预定义特性 AttributeUsage 主要用于标示自定义特性可以应用到哪些类型的程序元素上,这个信息由第一个参数给出。

规定该特性的语法如下:

[AttributeUsage(
     validon,               //规定特性可被放置的语言元素,它是枚举器 AttributeTargets 的值的组合,默认值是 AttributeTargets.All
     AllowMultiple=allowmultiple,   //如果为 true,则该特性可以在同一个元素多次使用,默认值是 false(不可多次使用)
     Inherited=inherited        //如果为 true,则该特性可被派生类继承,默认值是 false(不被继承)
)]

例如:

[AttributeUsage(AttributeTargets.Class |
          AttributeTargets.Constructor |
          AttributeTargets.Field |
          AttributeTargets.Method |
          AttributeTargets.Property, 
          AllowMultiple = true)]

 AttributeTargets :

 成员名称说明
  Assembly 可以对程序集应用特性。
  Module 可以对模块应用特性。
注意注意
Module 指的是可移植的可执行文件(.dll 或 .exe),而非 Visual Basic 标准模块。
  Class 可以对类应用特性。
  Struct 可以对结构应用特性,即值类型。
  Enum 可以对枚举应用特性。
  Constructor 可以对构造函数应用特性。
  Method 可以对方法应用特性。
  Property 可以对属性应用特性。
  Field 可以对字段应用特性。
  Event 可以对事件应用特性。
  Interface 可以对接口应用特性。
  Parameter 可以对参数应用特性。
  Delegate 可以对委托应用特性。
  ReturnValue 可以对返回值应用特性。
  GenericParameter 可以对泛型参数应用特性。
注意注意
目前,此特性可以应用仅于 C#、Microsoft 中间语言 (MSIL) 和发出的代码。
  All 可以对任何应用程序元素应用特性。

 按照上面的经验,再次开始动手来熟悉这一切,我指定了该自定义的特性不可继承,就在不解释别的了只是为了证明一下命名参数Inherited定性成功与否,总之还是很简单的。

namespace
{
    class Program
    {
        static void Main(string[] args)
        {
            GetAttributeInfo(typeof(OldClass));
            Console.WriteLine("==============");
            GetAttributeInfo(typeof(NewClass));
            Console.ReadKey();
        }
        public static void GetAttributeInfo(Type t)
        {
            OldAttribute myattribute = (OldAttribute)Attribute.GetCustomAttribute(t, typeof(OldAttribute));
            if (myattribute == null)
            {
                Console.WriteLine(t.ToString()+"类中自定义特性不存在!");
            }
            else
            {
                Console.WriteLine("特性描述:{0}\\n加入事件{1}", myattribute.Discretion, myattribute.date);
            }
        }
    }
 
   [AttributeUsage(AttributeTargets.Class,Inherited=false)]  //设置了定位参数和命名参数
    class OldAttribute : Attribute    //继承自Attribute
    {
        private string discretion;
 
        public string Discretion
        {
            get { return discretion; }
            set { discretion = value; }
        }
public DateTime date; public OldAttribute(string discretion) { this.discretion = discretion; date = DateTime.Now; } }
//现在我们定义两类 [Old("这个类将过期")]//使用定义的新特性 class OldClass { public void OldTest() { Console.WriteLine("测试特性"); } }
class NewClass:OldClass { public void NewTest() { Console.WriteLine("测试特性的继承"); } } //我们写一个方法用来获取特性信息 }

运行效果:

Conditional

这个预定义特性标记了一个条件方法,其执行依赖于它顶的预处理标识符。

它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。

#define DEBUG        //定义DEBUG宏,则下面有输出,不定义则没有输出
using System;
using System.Diagnostics;
public class Myclass
{
    [Conditional("DEBUG")]
    public static void Message(string msg)
    {
        Console.WriteLine(msg);
    }
}
class Test { static void function1() { Myclass.Message("In Function 1."); function2(); } static void function2() { Myclass.Message("In Function 2."); } public static void Main() { Myclass.Message("In Main function."); function1(); Console.ReadKey(); } }

ObsoleteAttribute

  这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

public ObsoleteAttribute(string message, bool error)

  参数             类型:

  message       System ..::.String        描述可选的变通方法的文本字符串。

 error          System ..::.Boolean       指示是否将使用已过时的元素视为错误的布尔值。

using System;
public class MyClass
{
   [Obsolete("Don\'t use OldMethod, use NewMethod instead", true)]
   static void OldMethod()
   { 
      Console.WriteLine("It is the old method");
   }
public static void Main()
   {
      OldMethod();      //将会报错
   }
}

自定义特性

  通过定义一个特性类,该特性类直接或间接地从Attribute派生,有助于方便快捷地在元数据中标识特性定义。特性类本身用一个特性-System.AttributeUsage特性来标记,这个是Microsoft定义的特性,AttributeUsage主要用于表示自定义特性可以应用到哪些类型的程序上。这些信息由它的第一个参数给出,该参数是必选的,其类型是枚举类型AttributeTarget。

一个新的自定义特性应派生自 System.Attribute 类,例如:
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
  AttributeTargets.Constructor |
  AttributeTargets.Field |
  AttributeTargets.Method |
  AttributeTargets.Property,
  AllowMultiple = true)]

public class DeBugInfo : System.Attribute    //已经声明了一个名为 DeBugInfo 的自定义特性。

让我们构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:

  • bug 的代码编号
  • 辨认该 bug 的开发人员名字
  • 最后一次审查该代码的日期
  • 一个存储了开发人员标记的字符串消息

我们的 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。

每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:

// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

public class DeBugInfo : System.Attribute
{
  private int bugNo;
  private string developer;
  private string lastReview;
  public string message;

  public DeBugInfo(int bg, string dev, string d)
  {
      this.bugNo = bg;
      this.developer = dev;
      this.lastReview = d;
  }

  public int BugNo
  {
      get
      {
          return bugNo;
      }
  }
  public string Developer
  {
      get
      {
          return developer;
      }
  }
  public string LastReview
  {
      get
      {
          return lastReview;
      }
  }
  public string Message
  {
      get
      {
          return message;
      }
      set
      {
          message = value;
      }
  }
}

应用自定义特性

通过把特性放置在紧接着它的目标之前,来应用该特性:

[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
  // 成员变量
  protected double length;
  protected double width;
  public Rectangle(double l, double w)
  {
      length = l;
      width = w;
  }
  [DeBugInfo(55, "Zara Ali", "19/10/2012",
  Message = "Return type mismatch")]
  public double GetArea()
  {
      return length * width;
  }
  [DeBugInfo(56, "Zara Ali", "19/10/2012")]
  public void Display()
  {
      Console.WriteLine("Length: {0}", length);
      Console.WriteLine("Width: {0}", width);
      Console.WriteLine("Area: {0}", GetArea());
  }
}

 

以上是关于C# 特性详解的主要内容,如果未能解决你的问题,请参考以下文章

C# 自定义特性(Attribute)详解

《C#零基础入门之百识百例》(一百)反射详解 -- 检索特性

《C#零基础入门之百识百例》(一百)反射详解 -- 检索特性

C#特性详解

C# 特性详解

C#自定义特性实例