zoukankan      html  css  js  c++  java
  • 【.net 深呼吸】自定义特性(Attribute)的实现与检索方法

    在.net的各个语言中,尤其是VB.NET和C#,都有特性这一东东,具体的概念,大家可以网上查,这里老周说一个非标准的概念——特性者,就是对象的附加数据。对象自然可以是类型、类型成员,以及程序集。

    说简单点,就是你在定义一些代码时,希望为某个代码对象加上一些额外的内容,但这些内容又不便在代码中直接写。比如,你为B类定义了一个 int 类型的属性P,而且是个虚属性,就是B的派生类可以重写它。我希望可以给这个属性弄个版本号,当子类override这个属性时,给它记一个版本号,然后在其他代码中访问这个属性时,可以检查一下版本号,比如当版本号小于3时,抛出异常,不让使用。

    另外比如,当某个类的某个成员在后续版本中会删除时,也可以使用特性来附加一个说明,好让调用代码识别,在.net类库中常用这种方法。在比如,可以给某些对象加上免调试的特性,当调用代码时,遇到带有这些特性的对象时就跳过调试。

    除了API库给出的特性外(特性类通常以Attribute结尾),我们也可以根据需要,自己定义特性类。

    自定义特性的定义和一般类的定义差不多,不过要注意两点:一是应该从Attribute类或其子类派生,一般是直接从Attribute类派生;二是,不管是不是直接派生自Attribute类,只要是特性类,都要在该类的定义上附加AttributeUsageAttribute特性,作用是指明你定义的这个特性的应用范围。

    啥意思呢,就是你这个特性能用在哪些对象上,比如这个特性只能用在方法上,那就把AttributeTargets值设为Method,如果只希望特性只用在类上,就指定Class;要是希望特性可以同时用在属性和方法上,就把Method和Property值组合起来。如果打算让这个特性成为“万能”通,那就用值All,表示特性可以用在所有代码对象上。

    下面,还是举个例子吧。假设我定义了这个特性。别管它干吗用的,反正只是个示例。

        [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
        public class AssRateAttribute : Attribute
        {
            ushort m_rate;
            public AssRateAttribute(ushort rate)
            {
                m_rate = rate;
            }
    
            public ushort Rate
            {
                get { return m_rate; }
            }
        }

    这里我指定了,它只能用于程序集,为啥选的程序集呢,因为它比较特殊,待会儿老周顺便介绍一下如何给程序集附加特性。

    还有两个属性,一个是Inherited,就是说这个特性是否可以被继承,当然,对于程序集而言,无所谓。不过,如果这个特性是用在类、类型成员上,就可以用上,我把这个特性用在某对象,它的派生类是否继承这个特性。要是设置为false,那么这个特性只有附加的目标类型上可以检索到,而在类型的派生类上是检索不到的。

    AllowMultiple指定该特性是否可以在同一个对象上多次使用。如果为false,那在附加时只能用一次,比如,下面的用法是会报错的。

    [AssRate(5)]
    [AssRate(9)]
    public class C
    {
    
    }

    因为上面的C类上,AssRateAttribute特性用了两次,就会出错。如果希望它可以多个实例并用,就把AllowMultiple属性改为true。当然,上面的代码只是假设,因为AssRateAttribute是不能用在类上的,它已指明只能用在程序集上。

    接下来,看看如何把特性贴到程序集上,程序集的特性用法比较特殊,因为它没有实体代码,只是个逻辑组合,因此,你听好了:请把用于程序集的特性,写在所有代码的前面

    就是说,如果你的代码文件前面写了代码,那就不能再为程序集加特性了。例如这样是不对的。

    namespace Test{
           ....
            public class DocumentsOpt{  .... }
    }
    
    [assembly:AssRate(12)]

    这样做是错的,因为前面已经有代码了,再为程序集附加特性是不可以的。

    正确的do法是这样的:

    [assembly:AssRate(12)]
    
    using System;
    using System.IO;
    namespace Test{ .... public class DocumentsOpt{ .... } }

    特性前面的assembly: 是啥意思呢,其实,特性的完整语法应该如下:

    [ <target:> <attributes....> ]

    例如要把特性用在类上,可以写上:

    [class: MyAttr ]

    不过,对于类,我们一般是可以省掉target,因为我们会在类的定义前面写上特性,这样编译器也能识别出来是附加到类上的。

    根据老周耍.net的多年经验,只有两种情况下才要写上target,其余情况可以省略。

    1、方法的返回值,因为这个特性虽然用在方法的返回值上,但是它只能写在方法的定义前,为了告诉编译器,这个特性不是给方法的,是给返回值的,所以要写上return,就像这样:[return: Attris...]。

    2、程序集,因为程序集是逻辑性的,不存在实体的代码对象,只能明确指定特性是用在程序集上的,即[assembly: MyAttr]。

    那么如何确保用于程序集的特性都能在所有代码之前呢,VS在项目中弄了这个结构,与项目本身有关的,都放在Properties节点下,其中有个代码文件叫AssemblyInfo.cs,或者AssemblyInfo.vb,这个文件不写其他的代码,只用来放程序集的特性。就像这样:

    [assembly: AssemblyTitle("app")]
    [assembly: AssemblyDescription("")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("")]
    [assembly: AssemblyProduct("app")]
    [assembly: AssemblyCopyright("Copyright © 老周 2016")]

    所以,我们刚刚定义的AssRateAttribute特性就要写在这个文件里。

    [assembly: AssRate(4)]

    为什么特性类要以Attribute结尾,除了便于识别,也看到在代码中使用时,可以省略Attribute。

    那么,特性应用之后,我们在运行阶段如何检索呢,这个嘛,你想啊,要动态知道对象的结构和信息,该用啥技术?对了,反射。就像你照镜子那样,可以把你的嘴脸呈现在镜子中,然后你就能看到自己了,道理一样,反射就是给代码照镜子用的,你可以运行时知道代码的结构,尽管代码已经编译了。

    比如下面的方法,检测我们刚定义的特性,看看特性指定的Rate值,然后做出响应。

            static void Check()
            {
                Assembly currassembly = Assembly.GetExecutingAssembly();
                AssRateAttribute att = currassembly.GetCustomAttribute<AssRateAttribute>();
                if (att != null)
                {
                    if (att.Rate <6)
                    {
                        throw new InvalidOperationException("程序集人品分太低,禁止使用。");
                    }
                    else
                    {
                        Console.WriteLine("程序集人品分合格。");
                    }
                }
            }

    反射API定义了多个GetCustomAttribute或,GetCustomAttributes方法,用来检索特性实例,这些方法包括多种扩展方法,我就不一一介绍,自己查MSDN和对象浏览器。如果在定义特性类时指明了它允许多个实例,就用GetCustomAttributes方法来获取多个实例,如果AllowMultiple为False,只允许单一实例,就用GetCustomAttribute方法获取单个特性实例。获取到特性实例后就可以进行验证或处理了。

    好,本文内容就扯到这里了。哦,关于示例源代码嘛,不提供,想玩的话,自己动手写。

  • 相关阅读:
    vue中使用 canvas给页面添加水印
    c++ get keyboard event
    sublime text c++ makefile
    dddd
    songwenxin
    wechat
    ddd
    log
    v3
    xiaoxiaole
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/5459105.html
Copyright © 2011-2022 走看看