学习参考:
- http://www.cnblogs.com/dudu/articles/4449.html
- http://www.cnblogs.com/anytao/archive/2007/04/19/must_net_03.html
今天在讨论IPC通信契约的时候,卢工提到使用Attribute来描述具体的接口方法的命令信息。发现对 Attribute的概念还不是很熟悉,因此对其进行学习梳理。
1、Attribute是什么?它有什么用?
先来看看官方的定义:
msdn文档对它的描述:公共语言运行时允许添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。
简单的定义: 本质上是一个类,其为目标元素提供关联附加信息,并在运行期以反射的方式来获取附加信息。具体的特性实现方法,在接下来的讨论中继续深入。
看定义总是有距离感,还是看看实际的应用吧。
2、常用场景
.NET中常见的Attribute:
- Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。
- DllImport:用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。
- Obsolete:这个属性用来标记当前的方法已经被废弃,不再使用了。
- Serializable:表明应用的元素可以被序列化
#define DEBUG //这里定义条件 using System; using System.Runtime.InteropServices; using System.Diagnostics; namespace AttributeDemo { class MainProgramClass { [DllImport("User32.dll")] public static extern int MessageBox(int hParent, string Message, string Caption, int Type); static void Main(string[] args) { DisplayRunningMessage(); DisplayDebugMessage(); MessageBox(0, "Hello", "Message", 0); Console.ReadLine(); } [Conditional("DEBUG")] private static void DisplayRunningMessage() { Console.WriteLine("开始运行Main子程序。当前时间是" + DateTime.Now); } [Conditional("DEBUG")] [Obsolete] private static void DisplayDebugMessage() { Console.WriteLine("开始Main子程序"); } } }
如果在一个程序元素前面声明一个Attribute,那么就表示这个Attribute被施加到该元素上,前面的代码,[DllImport]施加到MessageBox函数上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。
3、自定义特性
1、自定义的Attribute必须直接或者间接继承System.Attribute。
2、所有自定义的特性名称都应该有个Attribute后缀,命名规范为:"类名"+Attribute
3、使用AttributeUsage来限定你的Attribute 所施加的元素的类型,AttributeUsage本身也是一个Attribute。它有一个带参数的构造器,这个参数是AttributeTargets的枚举类型
public enum AttributeTargets { All=16383, Assembly=1, Module=2, Class=4, Struct=8, Enum=16, Constructor=32, Method=64, Property=128, Field=256, Event=512, Interface=1024, Parameter=2048, Delegate=4096, ReturnValue=8192 }
此外,AttributeUsage还定义了以下三个属性:
AllowMultiple::读取或者设置这个属性,表示是否可以对一个程序元素施加多个Attribute 。
Inherited:读取或者设置这个属性,表示是否施加的Attribute 可以被派生类继承或者重载。
ValidOn::读取或者设置这个属性,指明Attribute 可以被施加的元素的类型。
下面是一个自定义特性的例子,摘自这里:
using System; using System.Reflection; //应用反射技术获得特性信息 namespace Anytao.net { //定制特性也可以应用在其他定制特性上, //应用AttributeUsage,来控制如何应用新定义的特性 [AttributeUsageAttribute(AttributeTargets.All, //可应用任何元素 AllowMultiple = true, //允许应用多次 Inherited = false)] //不继承到派生类 //特性也是一个类, //必须继承自System.Attribute类, //命名规范为:"类名"+Attribute。 public class MyselfAttribute : System.Attribute { //定义字段 private string _name; private int _age; private string _memo; //必须定义其构造函数,如果不定义有编译器提供无参默认构造函数 public MyselfAttribute() { } public MyselfAttribute(string name, int age) { _name = name; _age = age; } //定义属性 //显然特性和属性不是一回事儿 public string Name { get { return _name == null ? string.Empty : _name; } } public int Age { get { return _age; } } public string Memo { get { return _memo; } set { _memo = value; } } //定义方法 public void ShowName() { Console.WriteLine("Hello, {0}", _name == null ? "world." : _name); } } //应用自定义特性 //可以以Myself或者MyselfAttribute作为特性名 //可以给属性Memo赋值 [Myself("Emma", 25, Memo = "Emma is my good girl.")] public class Mytest { public void SayHello() { Console.WriteLine("Hello, my.net world."); } } public class Myrun { public static void Main(string[] args) { //如何以反射确定特性信息 Type tp = typeof(Mytest); MemberInfo info = tp; MyselfAttribute myAttribute = (MyselfAttribute)Attribute.GetCustomAttribute(info, typeof(MyselfAttribute)); if (myAttribute != null) { //嘿嘿,在运行时查看注释内容,是不是很爽 Console.WriteLine("Name: {0}", myAttribute.Name); Console.WriteLine("Age: {0}", myAttribute.Age); Console.WriteLine("Memo of {0} is {1}", myAttribute.Name, myAttribute.Memo); myAttribute.ShowName(); } //多点反射 object obj = Activator.CreateInstance(typeof(Mytest)); MethodInfo mi = tp.GetMethod("SayHello"); mi.Invoke(obj, null); Console.ReadLine(); } } }
FlagsAttribute属性就是枚举类型的一项可选属性。它的主要作用是可以将枚举作为位域处理,所谓位域是单个存储单元内相邻二进制位的集合。在.Net framework中有很多枚举都是用FlagsAttribute特性修饰,例如:正则表达式选项System.Text.RegularExpressions.RegexOptions、文件监视中的文件改变类型System.IO.WatcherChangeTypes、System.Web.UI.WebControls.DataControlRowState等等。
使用FlagsAttribute需要注意:
1、只有要对数值执行按位运算(AND、OR、XOR)时才对枚举使用 FlagsAttribute 自定义属性。
2.、必须用 2 的幂(即 1、2、4、8 等)定义枚举常量。
using System; class FlagsAttributeDemo { enum Color1 : short { Black = 0, Red = 1, Green = 2, Blue = 4 }; [FlagsAttribute] enum Color2 : short { Black = 0, Red = 1, Green = 2, Blue = 4 }; static void Main() { Console.WriteLine("测试未使用FlagsAttribute属性"); Color1 MyColor1 = Color1.Red | Color1.Blue & Color1.Green; //我先不运行计算一下看看是那个:0001|0100&0010=0001 应该是Red Console.WriteLine("MyColor1={0}", MyColor1); Color1 MyColor_1 = Color1.Red | Color1.Blue; //我先不运行计算一下看看是那个:0001|0100=0101 应该是5 Console.WriteLine("MyColor_1={0}",MyColor_1); Console.WriteLine("测试使用FlagsAttribute属性"); Color2 MyColor2 = Color2.Red | Color2.Blue; //我先不运行计算一下看看是那个:0001|0100=0101应该是Red,Blue Console.WriteLine("MyColor2={0}", MyColor2); Console.ReadKey(); } }