一、 引言
利用定制的Attribute,我样可以声明性地为自己的代码添加注解,从而实现一些特殊的功能。定制的Attribute允许将定义的信息应用于几乎每一个元数据表记录项(AttributeTargets枚举了可应用的所有项)
当我们将一个定制的Attribute应用于某个元数据,例如应用于某个类时,则在编译后,会在该类的元数据中的CustomerAttribute“注册表”中添加定制的Attribute记录。
因此对于定制的Attribute的真正的用处,是需要两个保证的,其一是编译器将定制的Attribute信息写入元数据,这个保证是基础,其二,既然元数据表记录项的CustomerAttribute “注册表”注册了某些Attribute,则在运行时,我们可动态的获取某个记录项(类、方法、字段等)中应用的Attribute实例,并获取实例中的信息,从而动态的改变代码的执行方式。
.NET的各种技术(Windows窗体、Web窗体、XML Web服务等)它们都应用了定制的Attribute,目的是方便开发者在代码中表达他们的意图
二、 定义Attribute在元数据中的体现
这一节原本是可以略过的,但是我觉得我还是有必要单独在一节里面对我在上面讲到的“编译后,会在该类的元数据中的CustomerAttribute“注册表”中添加定制的Attribute记录“这一句作个解释,了解这个,我觉得对去理解Attriubte的使用,有些很重要的意义。
举个例子来说明,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Attribute_Demo
{
//[AttributeUsage(AttributeTargets.Class)]
public class CClassAttribute : Attribute
{
}
class Program
{
static void Main(string[] args)
{
}
}
}
此时注意CClassAttribute类,此时它没有应用于任何Attribute,我们来看一下编译器对它生成的元数据(ILDASM反编译器打开生成的exe,CTRL+M打开MetaInfo)如下:
然后我们将取消注释的定制属性代码,生成后再次查看CClassAttribute的元数据,如下:
我们对比一下CClassAttribute类型应用了属性AttributeUsageAttribute前后生成的元数据,显然编译器在生成应用了定制属性的AttributeUsageAttribute的CClassAttrubte元数据时,会将定制属性记录到CClassAttriubte的CustomAttribute的“注册表”中。
因此,对于类型、方法、字段等,可以在运行时,从它们的CustomAttribute的“注册表“中,查询是否应用了某些定制属性,从而动态的改变代码的执行方式。
三、 使用定制Attribute
C#允许将attribute应用于对以下任何目标元素进行定义的源代码:程序集、模块、类型(类、结构、委托、枚举、接口)、字段、方法(含构造器)、方法参数、方法返回值、属性、事件和泛型参数)
应用一个attribute时,C#允许一个前缀指定attribute应于的目标元素,许多情况,我们可以省略前缀,编译器一样能判断一个attribute应用于的元素目标,但有些情况,必须要指定一个前缀,来指定编译器清楚我们的意图,例如,当一个属性应用于事件时,如果不指定一个前缀,则编译器默认该属性只应用于事件,然而我们原本的意思是要将该属性应用于字段(事件的本质是一个私有的委托字段和Add和Remove方法)
下面的代码中,详细的例出所有可以应用的前缀,以及不能省略前缀的情况:
[assembly: SomeAttr] //应用于程序集 前缀不能省略
[module: SomeAttr] //应用于模块 前缀可以省略
[type: SomeAttr] //应用于类型 前缀可以省略
internal class SomeType
{
[field: SomeAttr] //应用于字段 前缀可以省略
public Int32 SomeField = 0;
[return: SomeAttr] //应用于返回值 前缀不能省略 如省略编译器默认应用于方法
[method: SomeAttr] //应用于方法 前缀可以省略
public Int32 SomeMethod([param: SomeAttr] //应用于参数 前缀可以省略
Int32 para)
{
return para;
}
[property: SomeAttr] //应用于属性 前缀可以省略
public Int32 SomeProp
{
[method: SomeAttr]//应用于get访问器方法,前缀可以省略
get { return SomeField; }
}
[event: SomeAttr] //应用于事件 前缀可以省略
[field: SomeAttr] //应用于编译器生成的委托字段 前缀不能省略 如省略编译器默认应用于事件
[method: SomeAttr]//应用于编译器生成的add和remove方法,前缀不能省略,如省略编译器默认应用于事件
public event EventHandler SomeEvent;
}
通过上面的示代码示例,我们知道,当attribute应用于某一项时,编译器会默认指定它应用的实际项,例如当一个attribute应用于字段时,此时可以将field前缀省略,因为这个属性只能作用于字段,但如果把一个作用于field的attribute应用于事件时,如果省略field前缀,则编译器默认作用于事件本身,这就与我们原本的意图不相符,因此在这种情况下,我们需要显示指定一个attribute前缀。
在介绍定制的Attribute之前,我们先来了解下attribute的使用规则
1、 定制attribute为必须直接或者间接地从公共抽象类System.Attribute派生
2、 attribute必须有一个公共构造器
3、 attribute可能支持一些特殊的语法,允许你设置与attribute类关联的公共字段或属性
4、 多个attribute可应用于单个目标元素
5、 可将每个attribute都封闭到一对方括号内,也可在一对方括号内以多个逗号分隔的attribute,如果attribute类构造器不获取参数,圆括号就是可有可无
6、 attribute类的后缀”Attribute”是可选的
四、 定义自己的attribute类
我们定义一个attribute类如下:
internal class TableAttribute : Attribute
{
public TableAttribute()
{
}
}
此时我们定义了一个TableAttribute类,此时这个attribute没有什么实际意义
1、 它从Attribute派生
2、 它有一个公共的构造函数
3、 为了保持与标准的相容性,TableAttribute类有一个Attribute后缀,但这不是必须的,所以你完全可以定义类名为TableSomeSuffex或者其他一些名称
最重要的这个TableAttribute可以用于任何目标元素,因此问题出现了,有时候我们想定义一个attribute只用于类,或者字段,而不想它用于方法,这时就需要一个限制,例如.NET的内置的Attribute,FlagAttribute只能用于struct,而不能作用于类或者字段
这时我们有必要去了解一个只能作用于attribute的attribute类---AttributeUsageAttribute
这里有点呦口,意思是指:
它只能作用于Attribute派生的类,这里隐藏了两个信息
1、 它只能作用于类(class)
2、 它只能作用派生自Attribute的类
例如下面的用法,是错误的,编译器会抛出:特性AttributeUsageAttribute仅在从System.Attribute派生的类有效
[AttributeUsage(AttributeTargets.Class)] //错误,User并不派生于Attribute
internal class User
{
}
AttributeUsageAttribute作用于定制的attribute,它主要用于指定attribute应用的范围。
我们来看下AttributeUsageAttribute的实现:
/// <summary>
/// AttributeUsageAttribute本身也是派生自Attribute
/// </summary>
public sealed class AttributeUsageAttribute : Attribute
{
/// <summary>
/// 公共构造器,获取一个AttributeTargets枚举
/// </summary>
/// <param name="validOn"></param>
public AttributeUsageAttribute(AttributeTargets validOn);
/// <summary>
/// 设置被AttributeUsageAttribute作用的attribute能否多次应用于同一目标,默认值为false
/// </summary>
public bool AllowMultiple { get; set; }
/// <summary>
/// 设置被AttributeUsageAttribute作用的attribute应用于基类时,是否同时应用于派生类和重写的方法,默认值为true
/// </summary>
public bool Inherited { get; set; }
/// <summary>
/// AttributeTargets作为一个标志位,用来限制被AttributeUsageAttribute作用的attribute的应用范围,此处可以组合枚举变量,使之能用于多种目标类型
/// 默认为AttributeTargets.All
/// </summary>
public AttributeTargets ValidOn { get; }
}
光看上面的注释,我们可能还不太了解,我们先看一个例子吧:
/// <summary>
/// SomeAttribute 被标记为:
/// 1、只能应用于类与方法
/// 2、不能重复应用于同一目标元素
/// 3、可以被子类继承
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
internal class SomeAttribute : Attribute
{
public SomeAttribute()
{
}
}
/// <summary>
/// SomeAttribute被标记为应用于类与方法
/// </summary>
[Some]
[Serializable]
public class BaseClass
{
/// <summary>
/// SomeAttribute被标记为应用于类与方法
/// </summary>
[Some]
public virtual void DoSomething() { }
}
/// <summary>
/// DerivedClass隐性拥有SomeAttribute特性,因为SomeAttribute被标记为可继承的
/// DerivedClass不拥有SerializableAttribute特性,因为SerializableAttribute被标记为不可继承的
/// </summary>
public class DerivedClass : BaseClass
{
/// <summary>
/// DoSomething隐性拥有SomeAttribute特性,因为SomeAttribute被标记为可继承的
/// </summary>
public override void DoSomething() { }
}
/// <summary>
/// 错误 SomeAttribute被标记为只应用于类与方法
/// </summary>
[Some]
public struct MYStruct
{
}
/// <summary>
/// 错误 SomeAttribute被标记为不能重复作用于目一目标
/// </summary>
[Some, Some]
public class MYClass
{
}
AttributeUsageAttribute应用于定制的attribute,从而限制attribute的应用目标类型,以及一些其他的限制
了解了AttributeUsageAttribute的用法后,我们来设计一个完整的Attribute,设计如下
1、 attribute只能作用于属性
2、 attribute拥有一个必须设置的string属性 ColumnName,同时还拥有一个可选Type 属性
3、 attribute不能重复应用于同一个属性
4、 attribute可以由派生类继承
设计代码如下:
/// <summary>
/// ColumnAttribute只能作用于Property
/// ColumnAttribute不能重复应用同一目标元素
/// ColumnAttribute可以被继承
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
internal class ColumnAttribute : Attribute
{
private string _ColumnName;
public string ColumnName
{
get { return _ColumnName; }
}
private Type _ColumnType =typeof(string);
/// <summary>
/// 可选参数
/// </summary>
public Type ColumnType
{
get { return _ColumnType; }
set { _ColumnType = value; }
}
/// <summary>
/// 必须传递一个参数用于设置TableName
/// </summary>
/// <param name="tn"></param>
public ColumnAttribute(string columnName)
{
_ColumnName = columnName;
}
}
internal class User
{
private string name;
/// <summary>
/// 映射Table中的UserName列,并且默认类型string
/// </summary>
[Column("UserName")]
public string Name
{
get { return name; }
set { name = value; }
}
public Int32 age;
/// <summary>
/// 映射Table中的UserAge列,设置命名参数为Int32类型
/// </summary>
[Column("UserAge",ColumnType=typeof(Int32))]
public Int32 Age
{
get { return age; }
set { age = value; }
}
}
定位参数与命名参数:
我们注意一下这行代码:
[Column("UserAge",ColumnType=typeof(Int32))]
大家觉得这样的语法是不是有点奇怪?如果单让我看这一行代码,我脑中闪现的第一想法是ColumnType=typeof(Int32) 这个ColumnType是ColumnAttribute构造器的一个参数,则这时是显式的指定参数名称,如果是这样的话,那这也没什么奇怪的了,但是我们看下ColumnAttribute,它只有惟一的一个参数,并没有ColumnType。
但是C#是允许这样特殊语法,来指定与ColumnAttribute有关的一些属性的
构造器的参数是“定位参数“ 是必须指定的
ColumnType属性是“命名参数“ 这种参数是可选的
Attribute构造器和字段、属性的数据类型
定义attribute类的实例构造器、字段、属性时,数据类型只能限制在一个小子集内,具体的话,合法的数据类型只有:
Boolean、Char、Byte、Sbyte、Int16、UInt16、Int32、UInt32、Int64、UInt64、Single、Double、String、Object、Type或者枚举类型,除此之外还可以使用上述任意类型的一维基0数组。
为什么合法的数据类型只能在上述类型之内呢?
这是因为在应用一个attribute时,必须传递一个编译时常量表达式
以上类型都可以拥有编译时常量表达式,例如Int字面常量 3,String字面常量“hello”,object常量null(object比较特殊,我们可以传递Int常量,但会发生装箱)
但是,数据类型是我们定义的结构或者类型,在应用attribute时,我们无法传递一个编译时的常量表达式。
例如下同定义的attribute,在应用时,编译器会提示错误:
using System.Linq;
using System.Text;
namespace Attribute_Demo
{
public class MYClass
{
}
public struct MYStruct
{
}
internal class SomeAttribute : Attribute
{
private MYClass mc;
private MYStruct ms;
public MYStruct MS
{
get { return ms; }
set { ms = value; }
}
public SomeAttribute(MYClass m1, MYStruct m2)
{
mc = m1;
ms = m2;
}
}
[Some(new MYClass(), new MYStruct())] //错误 new MYClass() 编译时,这不是一个常量
class Program
{
static void Main(string[] args)
{
Console.Read();
}
}
}
另外我们还要注意,如果传递的参数类型是Type,那我们必须要使用typeof运算符,因为typof(SomeType) 是编译时常量,但如果传递的是instance.GetType(),这是错误的,因为instance.GetType()是在运行时才能知道的常量
这些限制的最本质的原因其实是:一个attribute应用一个目标类型时, 在编译时就需要将attribute写入目标类型元数据中,所以传递的参数必须是编译时的常量,才有意义。
五、 检测定制attribute
1、 实现ICustomAttributeProvider接口的目标元素
ICustomAttributeProvider接口中有三个方法,用来检测目标元素应用的attribute
/// <summary>
/// 获取成员应用的所有特性实例
/// </summary>
/// <param name="inherit">搜索此成员的继承链以查找这些特性,则为 true;否则为 false</param>
/// <returns>一个包含所有自定义特性的数组,在未定义任何特性时为包含零 (0) 个元素的数组</returns>
object[] GetCustomAttributes(bool inherit);
/// <summary>
/// 获取成员应用指定的特性实例
/// </summary>
/// <param name="attributeType">要搜索的特性类型。只返回可分配给此类型的特性</param>
/// <param name="inherit">搜索此成员的继承链以查找这些特性,则为 true;否则为 false</param>
/// <returns>应用于此成员的自定义特性的数组;如果未应用任何特性,则为包含零 (0) 个元素的数组</returns>
object[] GetCustomAttributes(Type attributeType, bool inherit);
/// <summary>
/// 判断成员是否应用了指定的特性
/// </summary>
/// <param name="attributeType"> 自定义特性应用于的 System.Type 对象</param>
/// <param name="inherit">搜索此成员的继承链以查找这些特性,则为 true;否则为 false</param>
/// <returns> 如果此成员应用了一个或多个 attributeType 参数的实例,则为 true;否则为 false</returns>
bool IsDefined(Type attributeType, bool inherit);
以下类型都间接实现了ICustomAttributeProvider 接口:
Assembly
Module
ParameterInfo
Type
FieldInfo
PropertyInfo
MethodInfo
EventInfo
ConstructorInfo
因此,这些类的实例都可以调用ICustomAttributeProvider三个方法来获取应用的attribute属性实例
我们通过下面的代码,进一步的理解这几个方法的用法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Attribute_Demo;
[assembly: SomeAssembly("Attribute_Demo")]
namespace Attribute_Demo
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = true)]
internal class SomeAssemblyAttribute : Attribute
{
private string _AssemblyName;
public string AssemblyName
{
get { return _AssemblyName; }
}
public SomeAssemblyAttribute(string name)
{
_AssemblyName = name;
}
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
internal class SomeClassAttribute : Attribute
{
private string _ClassName;
public string ClassName
{
get { return _ClassName; }
}
public SomeClassAttribute(string name)
{
_ClassName = name;
}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
internal class SomeFieldAttribute : Attribute
{
private string _FieldName;
public string FieldName
{
get { return _FieldName; }
}
public SomeFieldAttribute(string name)
{
_FieldName = name;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
internal class SomePropertyAttribute : Attribute
{
private string _PropertyName;
public string PropertyName
{
get { return _PropertyName; }
}
public SomePropertyAttribute(string name)
{
_PropertyName = name;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
internal class SomeMethodAttribute : Attribute
{
private string _MethodName;
public string MethodName
{
get { return _MethodName; }
}
public SomeMethodAttribute(string name)
{
_MethodName = name;
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
internal class SomeParameterAttribute : Attribute
{
private string _ParameterName;
public string ParameterName
{
get { return _ParameterName; }
}
public SomeParameterAttribute(string name)
{
_ParameterName = name;
}
}
[AttributeUsage(AttributeTargets.Event, AllowMultiple = false, Inherited = true)]
internal class SomeEventAttribute : Attribute
{
private string _EventName;
public string EventName
{
get { return _EventName; }
}
public SomeEventAttribute(string name)
{
_EventName = name;
}
}
[SomeClass("BaseClass")]
public class BaseClass
{
[SomeField("classname")]
private string classname;
[SomeProperty("ClassName")]
public string ClassName
{
[SomeMethod("get_ClassName")]
get { return classname; }
}
[SomeEvent("m_event")]
[method: SomeMethod("add&remove")]
public event EventHandler m_event;
[SomeMethod("DoSomething")]
public void DoSomething([SomeParameter("str")] string str) { }
}
public class DerivedClass : BaseClass
{
}
class Program
{
static void Main(string[] args)
{
UseICustomAttributeProvider();
Console.Read();
}
static void UseICustomAttributeProvider()
{
Assembly ab = Assembly.GetExecutingAssembly();
//获取应用于当前运行程序集所有特性
object[] allAttr = ab.GetCustomAttributes(true);
Console.WriteLine("当前程序集拥有 {0} 个特性,如下:", allAttr.Length);
foreach (object att in allAttr)
{
Console.WriteLine(att.GetType());
}
Console.WriteLine("当前程序集是否拥有SomeAssemblyAttribute特性 :{0}", ab.IsDefined(typeof(SomeAssemblyAttribute), true));
//检测当前程序集SomeAssemblyAttribute的实例,可能有多个
object[] o = ab.GetCustomAttributes(typeof(SomeAssemblyAttribute), true);//Inherited 对于Assembly 无意义
Console.WriteLine("程序集名称 = {0}", ((SomeAssemblyAttribute)o[0]).AssemblyName);
//检测基类BaseClass的SomeClassAttribute
Type type = typeof(BaseClass);
o = type.GetCustomAttributes(typeof(SomeClassAttribute), true);
Console.WriteLine("基类类名 = {0}", (o[0] as SomeClassAttribute).ClassName);
//检测子类DerivedClass的SomeClassAttribute实例
Console.WriteLine("当Inherited = false时,DerviedClass能否检测SomeClassAttribute特性 :{0}", typeof(DerivedClass).IsDefined(typeof(SomeClassAttribute), false));
//检测方法DoSomething的SomeMethodAttribute实例
MethodInfo method = type.GetMethod("DoSomething");
o = method.GetCustomAttributes(typeof(SomeMethodAttribute), true);
Console.WriteLine("DoSomething方法名称 = {0}", (o[0] as SomeMethodAttribute).MethodName);
//检测方法DoSomething参数的SomeParameterAttribute实例
ParameterInfo para = method.GetParameters()[0];
o = para.GetCustomAttributes(typeof(SomeParameterAttribute), true);//Inherited 对于ParameterInfo 无意义
Console.WriteLine("参数名称 = {0}", (o[0] as SomeParameterAttribute).ParameterName);
//获取字段的SomeFieldAttribute实例
FieldInfo field = type.GetField("classname", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
o = field.GetCustomAttributes(typeof(SomeFieldAttribute), true);
Console.WriteLine("字段名称 = {0}", (o[0] as SomeFieldAttribute).FieldName);
//获取属性ClassName的SomePropertyAttribute实例
PropertyInfo property = type.GetProperty("ClassName");
o = property.GetCustomAttributes(typeof(SomePropertyAttribute), true);
Console.WriteLine("属性名称 = {0}", (o[0] as SomePropertyAttribute).PropertyName);
//获取属性getor访问器方法的SomeMethodAttribute实例
MethodInfo getor = property.GetGetMethod();
o = getor.GetCustomAttributes(typeof(SomeMethodAttribute), true);
Console.WriteLine("getor方法名称 = {0}", (o[0] as SomeMethodAttribute).MethodName);
//获取事件m_event的SomeEventAttribute实例
EventInfo e = type.GetEvent("m_event");
o = e.GetCustomAttributes(typeof(SomeEventAttribute), true);
Console.WriteLine("事件名称 = {0}", (o[0] as SomeEventAttribute).EventName);
}
}
}
输出:
小结:
1、调用ICustomAttributeProvider.GetCustomAttributes的方法返回的都是object[]数据
2、CustomAttributeProvider.GetCustomAttributes可适用于返回一个特性被标记可重复应用(AllowMultiple=true)的多个实例
3、它们的第二个参数Inherited对Assembly、ParameterInfo、Module无效(因为它们不存在继承)
2、 Attribute静态方法
除了上述讲到实现了ICustomAttributeProvider接口的类型中的三个获取与目标关联的attribute之外,Attribute类还提供了更加方便的三个静态方法,每个方法都有不同的重载版本,例如,每一个版本能操作类型成员(类、结构、枚举、接口、委托、构造器、方法、属性、字段、事件、返回类型)、参数、模板、程序集,还有一些版本允许告诉系统遍历继承层次结构,在结果中包含继承的attribute
我直接在《CLR VIA C#》中截了张图,懒的写了,如下:
我们基于前面的代码,我们用Attrribute来实现检测:
static void UseAttribute()
{
Assembly ab = Assembly.GetExecutingAssembly();
//获取应用于当前运行程序集所有特性实例
Attribute[] allAttr = Attribute.GetCustomAttributes(ab);
Console.WriteLine("当前程序集拥有 {0} 个特性,如下:", allAttr.Length);
foreach (Attribute a in allAttr)
{
Console.WriteLine(a.GetType());
}
//检测当前运行的程序集的SomeAssemblyAttribute实例
Console.WriteLine("当前程序集是否拥有SomeAssemblyAttribute特性 :{0}", Attribute.IsDefined(ab, typeof(SomeAssemblyAttribute)));
SomeAssemblyAttribute aa = Attribute.GetCustomAttribute(ab, typeof(SomeAssemblyAttribute)) as SomeAssemblyAttribute;
Console.WriteLine("程序集名称 = {0} ", aa.AssemblyName);
Type type = typeof(BaseClass);
//检测基类BaseClass的SomeClassAttribute实例
//如果BaseClass应用了多个SomeClassAttribute实例,方法会抛出一个异常
SomeClassAttribute ca = Attribute.GetCustomAttribute(type, typeof(SomeClassAttribute), true) as SomeClassAttribute;
Console.WriteLine("基类名称 = {0}", ca.ClassName);
//检测子类DerivedClass的SomeClassAttribute实例
Console.WriteLine("当输入参数Inherted = false时,DerviedClass能否检测SomeClassAttribute特性 :{0}", Attribute.IsDefined(typeof(DerivedClass), typeof(SomeClassAttribute), false));
//检测方法、属性、事件、字段等都与上面方法类似,省略
}
3、 方法比较及注意事项
1、 ICustomAttributeProvider.GetCustomAttributes(bool inherit) VS Attrribute. GetCustomAttributes
不同点:
1)、ICustomAttributeProvider.GetCustomAttributes 是一个接口实例方法,它只接收一个Inherted标识参数,而Attrribute. GetCustomAttributes是一个静态方法,它必须接收一个类型成员或者参数、程序集、模块,对于类型成员
2)、ICustomAttributeProvider.GetCustomAttributes只能获取目标所有attribute类型的实例
Attrribute. GetCustomAttributes即可以获取目标所有attribute类型的实例,也可以获取指定attribute类型的所有实例
3)、ICustomAttributeProvider.GetCustomAttributes 返回的是object[]
Attrribute. GetCustomAttributes返回的是Attribute[]
2、 ICustomAttributeProvider. GetCustomAttributes(TypeattributeType, bool inherit) VS Attribute. GetCustomAttribute
不同点:
1)、ICustomAttributeProvider.GetCustomAttributes(Type attributeType, bool inherit)可以获取目标指定attribute的多个实例 Attribute. GetCustomAttribute只能获取目标指定attribute单个实例,并且,如果目标应用了指定attribute的多个实例,则该方法会抛出一个异常,例如如果将SomeClassAttribute应用两次BaseClass,然后再用Attribute. GetCustomAttribute来获取BaseClass的SomeClassAttribute实例时,会抛出System.Relection.AmbiguousMatchException异常
2)、ICustomAttributeProvider.GetCustomAttributes(Type attributeType, bool inherit) 返回一个object[],Attribute.GetCustomAttribute 返回单个Attribute实例
3、 注意事项:
当以无论ICustomAttributeProvider中的三个方法或者是Attribute中的三个静态方法很多的版本都需要传递一个bool inherit时,它是这样的逻辑:
如果inherit=false ,检测一个目标的指定attribute时,如果目标不存在attribute,则就不存在
如果Inherit=true, 检测一个目标的指定attribute时,如果目村不存在attribute,则遍历继承层次,如果继承层次中有应用此attribute时,则存在
注意以上逻辑的前提是,指定的attribute本身就被标记为可继承,否则对于inherit=true中,遍历继承层次就没有意义了。
另外,inherit标志,对于Assembly、Module、ParameterInfo 这几种类型没有意义
六、总结
此篇主要是介绍了Attribute的用法,以及Attribute的好处,如果使用Attribute得当,在运行中能带来极大的灵活,我们可以查询当前的attribute实例,然后采取相应的策略,使得程序的扩展性和灵活性变的更强。
此篇主要是在学习Attribute中的一些笔记,由于篇幅较长,对于Attribute的实际应用,打算在下一篇来讲一个简单的案例。