.NET中Attribute
什么是Attribute(特性)?和Property(属性)是什么区别?
我们来看看MSDN中对特性的描述:
Attribute 类将预定义的系统信息或用户定义的自定义信息与目标元素相关联。目标元素可以是程序集、类、构造函数、委托、枚举、事件、字段、接口、方法、可移植可执行文件模块、参数、属性、返回值、结构或其他特性。特性在您编译代码时被发送到元数据中,并可通过运行时反射服务用于公共语言运行时以及任何自定义工具或应用程序。通俗地理解可以这么表述:你可以通过Attribute将一些额外信息加在一些目标元素上(类,字段,接口等),程序编译的时候就将这些额外的信息系列化程序集的元数据中,当你运行程序的时候可以通过反射技术从程序集元数据中读取这些额外信息,并根据这些额外信息决定你程序的行为。
Attribute和Property有什么区别?其实这个问题是针对中文背景的开发者而言的,因为很多中文译本把Attribute和Property都翻译成属性,在这里为了区分,我们把Attribute翻译为特性,Attribute和Property基本没有什么瓜葛,因为它们是.NET中不同层面的东西,Property就是我们再熟悉不过的定义在类中的属性,它属于面向对象理论范畴,而Attribute是编程语言文法层面的东西,其定义在上面一段已经描述。
你使用过.NET定义好的Attribute吗?
在.NET的基础类库中提供了很多定制好的Attribute供开发人员使用,这些定制的Attribute目的的方便开发者在代码中表达他们的意图。如下面三个Attribute类都是C#编译器能够理解的特性类:
Obsolete:这个属性用来标记不再使用的程序实体(如类或方法),每次使用标记为过时的实体时,会设设定此特性的方法,产生警告或错误。
Conditional:该特性可以标示出某种环境设置下某个方法是否应该被调用。
Serializable:指示一个类可以序列化。
下面以Obsolete特性的使用为例,说明Attribute是如何应用它的目标元素的。
namespace AttributeDemo
{
class Program
{
static void Main(string[] args)
{
MyClass myclass = new MyClass();
myclass.OldMethod();
Console.ReadKey();
}
}
public class MyClass
{
[Obsolete("这是一个旧的方法,请调用新的方法NewMethod")]
public void OldMethod()
{
Console.WriteLine("这是旧方法");
}
public void NewMethod()
{
Console.WriteLine("这是新方法");
}
}
}
调试这段程序的时候会发出警告信息,如下图所示:
像Obsolete这样的定制特性,编译器能够做出相应处理,如在使用标记了Obsolete特性的方法时会发出警告信息,但如果我们自己定制的Attribute时,编译器会做什么处理呢。下面我们自己定义一个Attribute。
我也来定义一个Attribute
为了符合“公共语言规范”(CLS),定制Attribute必须直接或间接从公共抽象类System.Attribute派生。所以我们前面提到Obsolete、Conditional和Serializable都是派生于Attribute。这里需要说明下的是自定制的Attribute的命名规范,其规则是“特性名+Attribute”,也就是我们自定制必须以Attribute为后缀,那么我们上面提到的三个特性都没有Attribute为后缀的呢,原来定义它们的时候都是有Attribute后缀的,如Obsolete是ObsoleteAttribute,只是我们将一个特性应用于某个目标元素时可以将Attribute这个后缀去掉,因为编译器会先查找没有Attribute后缀的特性,如果没有找到,则会查找加了Attribute后缀的特性名称。
System.Attribute类的构造器被protected修饰,说明它不能自己实例化,只能被它的派生类调用。它有三个重要的静态方法,如下:
方法名称 |
说明 |
GetCustomAttributes |
有多个重载,返回作用于目标的Attribute类实例的数组,也就是返回的类型是Attribute[] |
GetCustomAttribute |
有多个重载,返回作用于目标的Attribute类的一个实例,如果目标没有应用任何的Attribute则返回null,如果目标应用了指定的Attribute的多个实例,就抛出一个System.Reflection.AmbiguousMatchException异常。 |
IsDefined |
如果至少有一个指定的Attribute派生类实例作用于目标,就返回true,否则返回false。这个方法效率很高,因为它不构建Attribute的实例,前面的两个方法返回都是Attribute实例,也就是需要从元数据中获取信息来构建实例,耗费性能多 |
通常检查一个目标元素是否被应用了某个Attribute时,就调用System.Attribute.IsDefined方法,因为它的性能比GetCustomAttributes和GetCustomAttribute要高,如果需要返回Attribute的实例,则调用GetCustomAttributes或GetCustomAttribute方法。调用这三个方法都会扫描托管模块的元数据(因为Attribute是在编译的时候保存在托管模块的元数据上的),执行字符串比较来定义指定的Attribute类。这样的操作对时间性能消耗大,如果需要反复调用这些方法,可以缓存这些方法的调用结果,也就是把实例保存在全局变量中,不需要每次都扫描和构造实例。
除了System.Attribute类提供的上面的三个静态方法可以检查目标元素应用Attribute的情况外,System.Reflection命名空间定义的一些类也允许你检查一个模块的元数据的内容,这些类包括Assembly,Module,ParameterInfo,MemberInfo,Type,MethodInfo,ConstrucorInfo,FieldInfo,EventInfo,PropertyInfo等,它们都提供了GetCustomAttributes和IsDefined方法。这些类GetCustomAttributes返回的类型是Object[],而System.Attribute类GetCustomAttributes方法返回的类型是Attribute[]。
下面将分别使用System.Attribute和System.Reflection.Type各自提供的GetCustomAttributes方法获取Attribute实例提供示例代码,以让您有一个更加直观的认识。
定义一个Attribute
public class MyMsgAttribute:Attribute
{
public string Msg { get; set; }
public MyMsgAttribute(string msg)
{
补充:Web开发 , ASP.Net ,