C#回顾 –6.特性

 

1.特性是什么?

 Attribute 用来对类、属性、方法等标注额外的信息,贴一个标签(附着物) 
通俗:给 类 或 类成员 贴一个标签,就像航空部为你的行李贴一个标签一样 
个人理解,特性就是修饰对象元数据的修饰符。
1.是特性             2.是访问修饰符 
3.声明修饰符     4.数据类型 
5.变量名             6.变量数据值
其中1、2、3、4、5就是元数据,用来描述数据(6)的数据。

2.特性到底是什么?

如上面的 Obsolete  ,会不会也是一个如 public  static 这样类似的修饰符呢,我们且看看反编译后的中间语言。
 意料之外,我们看到了上面的2、3、4、5,而1(特性)怎么跑到里面去了,且是一种看不懂的东东,反正我们知道了不是类似的修饰符。
然后我们接着在vs里面把光标移到 Obsolete  上按F12,如:

 原来只是一个继承了 Attrbute 的一个类(class)。那么上面我们看不懂的部分应该就是这个 ObsoleteAttribute 类的实例化了。

我们来回答上面的问题:特性到底是什么?特性只是一个类而已。


3.自定义特性

我们看到上面系统特性 Obsolete 上面还有特性,如: Serializable  AttributeUsage  Camvisible 等。像这种特性我们称之为“元数据的元数据”(元元数据)。
1.我们分别来解释性上面的三个特性。
 Serializable :表示类型支持序列化。
 ComVisible :微软定义“控制程序集中个别托管类型、 成员或所有类型对COM的可访问性”。
 AttributeUsage :这个比较重要了,基本上每个特性定义都用到了它。它就是用来表示当前这个特性可用于哪些对象。如:类、方法、属性...等等。(只需要用到这个我们就可以自定义特性了)
2.上面有个问题,不知道大家发现没有。
就是我们特性名明明是 Obsolete  ,为什么我们F12进去后变成了 ObsoleteAttribute 呢?这其实只是一个微软的约定而已,没有为什么。
其实我们可以两种写法: [ObsoleteAttribute("已过时")]  和  [Obsolete("已过时")]  是等效的,只是我们一般都用后面这种。
3.定义的特性必须继承于 Attribute 
4.属性没有 set 方法。只能通过构造函数赋值。(这是因为特性语法所致,因为特性的定义只存在单行的中括号中,不能实例化之后在设置属性,所以全部的设置都在后面的小括号里进行的。如果需要有 set 属性,我们就要用到命名参数,下面会继续讲到)
好了,我们通过这四点完全可以自己定义个特性来玩玩了。我们来定义一个给机器看的注释。我们平时的注释都只是给程序员看的,编译之后就全没了。那我们想在代码运行时,弹出我们的注释怎么办,接下来我们用自定义特性来实现,如:

  1. [AttributeUsage(AttributeTargets.All)] //设置可以用于那些对象  

  2. public class TMessgAttribute : Attribute  

  3. {  

  4.     public TMessgAttribute()  {    }  

  5.   

  6.     public TMessgAttribute(string createTime, string createCreatename, string mess)  

  7.     {  

  8.         this.createTime = createTime;  

  9.         this.mess = mess;  

  10.         this.createname = createCreatename;  

  11.     }  

  12.   

  13.     private string createname;  

  14.   

  15.     private string mess;  

  16.   

  17.     private string createTime;  

  18.   

  19.     public string CreateTime  

  20.     {  

  21.         get { return createTime; }  

  22.     }  

  23.   

  24.     public string Mess  

  25.     {  

  26.         get { return mess; }  

  27.     }  

  28.   

  29.     public string Createname  

  30.     {  

  31.         get { return createname; }  

  32.     }  

  33. }  



上面是自定义的特性,怎么使用,和系统特性一样,先定义个测试类,
  1. [TMessg("2016-12-15""tangsansan""我,我只是测试自定义特性,不要报错哦")]  

  2. public class TClass  

  3. {  

  4.   

  5. }  


我们定义了特性,也使用了特性,然我们却不知道怎么看效果。我们想看到效果怎么办。可以使用反射看看 TClass 类的元数据,如:

  1. System.Reflection.MemberInfo info = typeof(TClass);  

  2. /* 

  3.  * 方法一 

  4.  * Attribute.GetCustomAttribute 方法 (MemberInfo, Type) 

  5.  * 检索应用于类型成员的自定义参数。参数指定成员和要搜索自定义成员的类型 

  6.  *        element:一个从 MemberInfo 类派生的对象,该类描述类的构造函数、事件、字段、方法或属性成员。(指定应用了特性的类)。

  7.  *  attributeType:要搜索的自定义属性的类型或基类型 

  8.  */  

  9. TMessgAttribute attr = (TMessgAttribute)Attribute.GetCustomAttribute(info, typeof(TMessgAttribute));  

  10. Console.WriteLine("类名:{0}", info.Name);  

  11. Console.WriteLine("创建时间{0}", attr.CreateTime);  

  12. Console.WriteLine("创建人{0}", attr.Createname);  

  13. Console.WriteLine("消息{0}", attr.Mess);  

  14. Console.WriteLine("-------------");  

  15. /* 

  16.  * 方法二 

  17.  * MemberInfo.GetCustomAttributes 方法 (Type, Boolean) 

  18.  * attributeType:要搜索的属性的类型。 仅返回可分配给此类型的属性。 

  19.  *       inherit:true 搜索此成员继承链,以查找这些属性;否则为 false。 属性和事件,则忽略此参数。 

  20.  */  

  21. object[] attrs = info.GetCustomAttributes(typeof (TMessgAttribute), false);  

  22. TMessgAttribute mes = attrs[0] as TMessgAttribute;  

  23. Console.WriteLine("创建时间{0}", mes.CreateTime);  

  24. Console.WriteLine("创建人{0}", mes.Createname);  

  25. Console.WriteLine("消息{0}", mes.Mess);  


4.什么是命名参数?

上面的自定义特性都是通过构造函数设置字段私有字段,然后通过只提供了 get 的属性来访问。那么可否直接在特性里面定义拥有 get  set 的属性吗?答案是肯定的。那怎么在使用特性的时候设置这个属性呢?我们接着往下看。
我们接着在自定义特性里面添加一个属性。
  1. //修改时间  

  2. public string modifyTime{get;set;}  

使用自定义特性。
  1. [TMessg("2016-12-15""tangsansan""我,我只是测试自定义特性,不要报错哦",modifyTime="2016-11-26")]  

  2. public class TClass  

  3. {  }    

我们发现,直接在输入了构造函数之后接着设置属性就可以。(这就相当于可选参数了,属性当然可以随便你是否设置了。不过这里需要注意了,前面的参数一定要按照定义的特性构造函数的参数顺序)

这种参数,我们称为命名参数。

6.我们来继续要看看AttributeUsage    (这个描述特性的特性--“元元数据”)




我们来看看他的这几个属性是干嘛的。从最后一个开始看。

1. AttributeTargets 

我们在上面其实就已经看到并也已经使用了。



我们设置的是可用于所有对象。 AttributeTargets 其实是个枚举,每个值对于一个类型对象。
你可以直接在 AttributeTargets F12进去:


 我们看到了每个值代表可以用于所对于的对象类型。

2. Inherited (是一个布尔值)
“如果该属性可由派生类和重写成员继承,则为 true ,否则为 false 。 默认值为 true 
如下,我们设置 Inherited = false 那么继承 TClass 的 T2Class 无法访问到 TClass 中设置的特性元数据。

如果我们想要这样设置怎么办。在 AttributeUsage 中设置 AllowMultiple = true 如:


3. AllowMultiple (也是一个布尔值)
“如果允许指定多个实例,则为 true ;否则为  false 。 默认值为 false 。”
我们设置两个特性试试,如:


反之,我们设置 Inherited = true (或者不设置任何,因为默认就是 true )打印如下:

如果我们想要这样设置怎么办。在 AttributeUsage 中设置 AllowMultiple = true 如:


1.添加一个继承类
  1. public class T2Class : TClass  

  2. {  

  3.     //...........  

  4. }  


2.更新
  1. [AttributeUsage(AttributeTargets.All , Inherited=false)] //设置可以用于那些对象    

  2. public class TMessgAttribute : Attribute  

  3. {  

  4.         //...  

  5. }


3.输出

但是要报错,


打印地方的代码需要修改。因为之前是打印一个特性信息,这里是打印一个特性数组集合的信息。
  1. TMessgAttribute[] attrs = (TMessgAttribute[])Attribute.GetCustomAttributes(info, typeof(TMessgAttribute));  

  2. foreach (var item in attrs)  

  3. {  

  4.     Console.WriteLine("类名:{0}", info.Name);  

  5.     Console.WriteLine("创建时间{0}", item.CreateTime);  

  6.     Console.WriteLine("创建人{0}", item.Createname);  

  7.     Console.WriteLine("消息{0}", item.Mess);  

  8.     Console.WriteLine("-------------");  

  9.     Console.WriteLine("修改时间{0}", item.modifyTime);  

  10. }  



7.自定义特性可以干什么?

上面我们通过反编译,发现自定义特性实际上就是一个对象调用的最前面加了一段实例化的代码。
 
那么我们可以做的事可多了,除了像上面一样为对象设置“注释”,我们还可以自定义个特性,给某些方法或是某些类做“操作日志记录”,或者给需要在执行某些方法的时候需要权限,我们可以做个权限认证的特性等等。
这里就需要大家自己去扩展了。



农码一生 一、特性是什么东东

http://www.cnblogs.com/zhaopei/p/attribute_is_what.html






原文地址:https://www.cnblogs.com/tangge/p/6101740.html