CLR学习笔记--Attribute

本文详细介绍了Attribute在.NET中的应用,包括元数据体现、使用定制Attribute、定义自己的attribute类、检测定制attribute的方法,强调了Attribute在代码中的重要性和使用规则。文中通过示例代码阐述了Attribute的构造、限制及其在运行时的动态查询和处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、       引言

利用定制的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的实际应用,打算在下一篇来讲一个简单的案例。

 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值