Attribute是什么
-
Attribute的中文姓名
为什么我要拿一段文字来说Attribute的中文姓名呢?答案是:因为这很重要。正所谓“名”不正,则言不顺;另外重构手法中有一种很重要的方法叫重命名,由此我们可以知道名称的定义是非常重要的。
Atrribute的中文姓名是特性,有些人也可能称之为属性;对于初学c#的人来说,可能很少接触到Atrribute,但Property想必一入门就有接触,而Property的中文姓名就叫属性,所以当我们说起属性时,自然而然就会想起Property,为了避免名称上的混淆,所以个人觉得在c#中还是把Attribute称之为特性为好。
对于刚接触Attribute时,我想大部分人会和我有一个一样的疑问就是Attribute和Property有什么关联呢?我想会产生这样的疑问就是因为中文名称的原因(在HTML里,一般把Attribute称之为属性)。如果你要问我这个问题的话,我的回答是——相当于Attribute和Class关联,我想你应该不会问我Attribute和Class有什么关联吧。
-
Attribute做什么用的?
MSDN中对Attribute的定义是:Attribute 类将预定义的系统信息或用户定义的自定义信息与目标元素相关联。目标元素可以是程序集、类、构造函数、委托、枚举、事件、字段、接口、方法、可移植可执行文件模块、参数、属性 (Property)、返回值、结构或其他属性 (Attribute)。
Attribute就是为目标元素提供附加信息的,简单来说Attribute就是为了为程序添加注释的,以进一步阐述其目标元素,那与我们平时添加的注释有什么区别呢?其区别就是注释是给人看的,程序在编译时会把注释忽略的,而Attribute是为程序注释的,所以编译器会把Attribute编译进程序集(Assembly)。
如果你还没弄懂Attribute是做什么用的话,你可以参考,我就不再赘述了。
自定义Attribute
[AttributeUsage(AttributeTargets.Property| AttributeTargets.Class, AllowMultiple = true)] public class ColumnAttribute : Attribute { public string ColumnName { get; set; } public string ColumnAlias { get; set; } public ColumnAttribute(string columnName) { this.ColumnName = columnName; } }
上面代码是我自定义的一个ColumnAttriubte类。所有自定的Attribute的必须继承自抽象类Attribute,就如上面代码所示。
下面我们来看一下如何把我们刚刚定义ColumnAttribute类使用上:
public class SysUserInfo { [Column("UserID", ColumnAlias = "用户账号")] public string UserID { get; set; } [ColumnAttribute("UserID")] [ColumnAttribute("UserID", ColumnAlias = "用户账号")] public string UserID { get; set; } [ColumnAttribute("UserID"), ColumnAttribute("UserID", ColumnAlias = "用户账号")] public string UserID { get; set; } [ColumnAttribute("UserID", ColumnAlias = "用户账号"), ColumnAttribute("UserID")] public string UserID { get; set; } }
上面代码仅仅只为说明这四种写法都是等价的。从上面的代码中我们可以看出使用自定义特性类ColumnAttribute时,可以省略“Attribute”。在此说明一下定义特性类时,我们约定在其类名后添加“Attribute”,当然你也可以不加,程序也一样正常运行,但建议加上,原因一:这是一个约定;原因二:当你的Attribute施加到一个程序的元素上的时候,编译器先查找你的Attribute的定义,如果没有找到,那么它就会查找“Attribute名称"+Attribute的定义。
在这儿我强调一下特性是非静态类,那既然是非静态类的话,不管间接还直接最终都要实例化时才能起实际效果,那按上所说那特性类是在哪儿实现化的呢?像我们平常实例化一个对象我们都要用到关键字new,但上面的例子哪里有new关键字呢?没有,这就是Attribute实例化比较怪异的地方,Attribute的实例化是在方括号([ ])里调用构造函数完成的,而构造函数的参数就是在特性后面的括号中传进去的,如上面的“UserID”便是构造函数的参数;这儿还有一点怪异的地方就是它实例化对象的属性赋值,属性赋值它是直接传递给构造函数的参数后面进行赋值的,如上面的ColumnAlias="用户账号”。
AttributeUsageAttribute特性
从MSDN对Attribute的定义中我们看到特性是与目标元素相关联的,而目标元素的种类则多达十几种。那我们想想如果没有什么约束的话,那使用起来想必有些混乱。
那Attribute类是怎么去限制其附加到哪些目标元素的呢?它是通过AttributeUsageAttribute来做的,我们注意到AttributeUsageAttribute它也是Attribute,正所谓物以类聚。AttributeUsage类的作用就是帮助我们控制定制特性的使用。
转到其定义我们可以看到AttributeUsageAttribute中有三个属性:枚举类型AttributeTargets的ValidOn、布尔类型的AllowMultiple和Inherited。如下图:
- ValidOn
就是它用来指定自定义特性类只可以放在哪些程序实体前,如自定义Attribute那段的代码中自定义特性就限制只允许在属性中使用。从上图微软对AttributeUsageAttribute定义上我们看到其构造函数只有一个参数,那现在如果我们要限制我们自定义特性只允许在属性和字段上使用,我们应该怎么做呢?它是或”|“运算符来实现的,[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)],顺便我们先来看一下微软对AttributeTargets枚举的定义,这样你或许就能想明白为什么这样可以,如下图:
- Inherited
我们可以使用这个属性来控制定制特性的继承规则。它标记了我们的特性能否被继承。
- AllowMultiple
这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。
Attribute类
上面说了如何自定义Attribute类,以及如果使用自定义Attribute类。虽然说特性是写给程序自己看的,但它终归是拿来给人用的,那接下来我们来看看如何在程序中读取它里面的值呢?
我们可以转到Attribute类的定义,从中可以看到其中有名为GetCustomAttribute和GetCustomAttributes的静态方法,我们可以通过这两个静态方法可以读取到自定特性类的实例化对象。
static void Main(string[] args) { foreach (var pi in typeof(SysUserInfo).GetProperties()) { // ColumnAttribute columnAttr = pi.GetCustomAttributes(false)[0] as ColumnAttribute; //等价于下面的语句 ColumnAttribute columnAttr = Attribute.GetCustomAttribute(pi, typeof(ColumnAttribute)) as ColumnAttribute; if (columnAttr != null) { Console.WriteLine(columnAttr.ColumnName); } } }
以上是我对Attribute的一个小的总结,如果你觉得我写得哪儿有错误的话,还望不吝指点。