特性是一组用方括号包括的代码,可以为代码提供额外的信息。利用反射,可以为特性提供非常强大的功能。
当编译器遇到一个自定义特性时,会首先把Attribute字符串添加到特性名称后面,形成一个新的名称。如果特性 已经以Attribute结尾,则不会再添加字符串。得到该字符串之后,编译器会在可用的命名空间中查找该特性类,如果找不到该类或者找到的类和特性的用法不匹配,就会报告编译错误。
AttributeUsage特性
为了能够使自定义特性类可以使用,需要为其再添加一个系统定义的特性AttributeUsage,该特性定义了自定义特性该如何使用。
AttributeTargets枚举
可以指定自定义特性究竟应用于哪一层代码上。这个值是由AttributeUsage的第一个参数指定,它是AttributeTargets的枚举。AttributeTargets枚举的成员如下:
- All
- Assembly
- Class
- Constructor
- Delegate
- Enum
- GenericParameter
- Interface
- Method
- Module
- Parameter
- Property
- ReturnValue
- Struct
当自定义特性用Assembly或者Module指定的时候,需要应用到其他特性之前,这时候需要在自定义特性前面添加assembly或者module关键字。
[assembly:MyAttribute(Parameters)]
...
[module:MyAttribute(Parameters)]
也可以指定特性可以应用于多个元素上,这时候需要用|
将多个值组合起来。
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
class MyAttribute : Attribute
{
}
AllowMultiple参数
AllowMultiple参数也是AttributeUsage的一个参数,指定了特性能否多次作用于同一元素上。这是一个可选的参数。
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
class MyAttribute : Attribute
{
}
Inherited参数
Inherited参数指定特性是否可以被派生的类或接口继承。如果该参数指定为true的话,派生类或者重写的方法或属性就可以自动继承该特性。
这也是一个可选的参数。
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true)]
class MyAttribute : Attribute
{
}
自定义特性的参数
必选参数
必选参数会作为自定义特性类的构造函数的参数传递进去,因此要使用的必选参数的个数和顺序需要和构造函数中的参数个数和顺序相同。
可选参数
可选参数会作为自定义特性类的属性传递进去,因此需要自定义特性类中有和可选参数相同名称的属性或字段存在。
使用自定义特性
知道了这些,就可以编写自己的自定义特性了。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, AllowMultiple = true, Inherited = true)]
class CodeRecordAttribute : Attribute
{
private readonly string lastModifiedTime;
public CodeRecordAttribute(string LastModifiedTime)
{
lastModifiedTime = LastModifiedTime;
}
public string Author
{
get; set;
}
public string LastModifiedTime
{
get
{
return lastModifiedTime;
}
}
}
再写一个类,应用我们的自定义特性。
[CodeRecord("2015-9-1", Author = "Leo")]
class MyIncompleteWork
{
[CodeRecord("2015-9-10", Author = "Leo")]
public MyIncompleteWork()
{
}
[CodeRecord("2015-9-9")]
public void DoSomething()
{
}
}
最后通过反射来查看一下使用的特性。
internal static void AttributeTest()
{
//实例化一个类
MyIncompleteWork work = new MyIncompleteWork();
//获取它的Type对象
Type t = work.GetType();
//获取类上定义的特性
CodeRecordAttribute classAttribute = Attribute.GetCustomAttribute(t, typeof(CodeRecordAttribute)) as CodeRecordAttribute;
//查看类特性
if (classAttribute != null)
Console.WriteLine($"最后修改时间:{classAttribute.LastModifiedTime}, 作者:{classAttribute.Author}");
//获取构造函数上的特性
CodeRecordAttribute cntrAttribute = Attribute.GetCustomAttribute(t.GetConstructor(Type.EmptyTypes), typeof(CodeRecordAttribute)) as CodeRecordAttribute;
if (cntrAttribute != null)
Console.WriteLine($"最后修改时间:{cntrAttribute.LastModifiedTime}, 作者:{cntrAttribute.Author}");
//获取方法上的特性
CodeRecordAttribute methodAttribute = Attribute.GetCustomAttribute(t.GetMethod("DoSomething"), typeof(CodeRecordAttribute)) as CodeRecordAttribute;
if (methodAttribute != null)
Console.WriteLine($"最后修改时间:{methodAttribute.LastModifiedTime}, 作者:{methodAttribute.Author}");
}
}