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方法获取单个特性实例。获取到特性实例后就可以进行验证或处理了。

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

  • 相关阅读:
    Get distinct count of rows in the DataSet
    单引号双引号的html转义符
    PETS Public English Test System
    Code 39 basics (39条形码原理)
    Index was outside the bounds of the array ,LocalReport.Render
    Thread was being aborted Errors
    Reportviewer Error: ASP.NET session has expired
    ReportDataSource 值不在预期的范围内
    .NET/FCL 2.0在Serialization方面的增强
    Perl像C一样强大,像awk、sed等脚本描述语言一样方便。
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/5459105.html
Copyright © 2011-2022 走看看