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());
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值