zoukankan      html  css  js  c++  java
  • 有关C#标签Attribute的熟悉

    Attribute 简单用法:

      最近用到了,所以静下心来找些资料看了一下,终于把这东西搞清楚了。

    一.什么是Attribute

    先看下面的三段代码:

    1.自定义Attribute类:VersionAttribute

     [AttributeUsage(AttributeTargets.Class)]
        public class VersionAttribute : Attribute
        {
            public string Name { get; set; }
            public string Date { get; set; }
            public string Describtion { get; set; }
        }
    2.使用自定义Attribute的Class:
    
        [Version(Name = "hyddd", Date = "2009-07-20", Describtion = "hyddd's class")]
        public class MyCode
        {
            //...
        }
    3.上面这个Class中的Attribute一般会被如何使用呢?
    
        class Program
        {
            static void Main(string[] args)
            {
                var info = typeof(MyCode);
                var classAttribute = (VersionAttribute)Attribute.GetCustomAttribute(info, typeof(VersionAttribute));
                Console.WriteLine(classAttribute.Name);
                Console.WriteLine(classAttribute.Date);
                Console.WriteLine(classAttribute.Describtion);
            }
        }

    示例完毕!上面三段代码相信已经说明了Attribute大概是一个什么东西和怎么去用。

    二.深入讨论Attribute

    1.Attribute的概念定义

         关于Attribute概念的定义,我直接引用《你必须知道的.NET之特性和属性》中的一段来说明:

         MADN的定义为:公共语言运行时允许添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据(metadata)保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。

         我们简单的总结为:定制特性attribute,本质上是一个类,其为目标元素提供关联附加信息,并在运行期以反射的方式来获取附加信息。

    噢,原来Attribute的目的是为元素提供关联附加信息。其中,上面第一段代码中“[AttributeUsage(AttributeTargets.Class)] ”说明了Attribute提供附加信息的元素是Class,所以如果上面第二段的代码改为:

    public class MyCode
    {
        [Version(Name = "hyddd", Date = "2009-07-20", Describtion = "hyddd's class")]
        public void Test() { }
    }

    会出现编译错误。

    2.Attribute作为编译指令

         Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。所以在第三段代码中,你可以在没有实例化MyCode对象的情况下取到MyCode的Attribute信息。由于Attribute类是在编译的时候被实例化的,所以你还可以用外部工具维护这些Attribute信息。

    3.Attribute与Property

         从中文来说,Attribute和Property的中文都叫“属性”,很容易让人混淆。现在的文章,Attribute一般翻译为”特性”,而Property称为“属性”。

         或许你会问,我用静态的Property/Field一样可以做到在不实例化的时候拿到一些信息,如果这样的话,Attribute又有什么存在意义呢?

    1.Property:

         Property可以说是一个面向对象的概念,提供了对私有字段的访问封装,在C#中以get和set访问器方法实现对可读可写属性的操作,提供了安全和灵活的数据访问封装。比如:

    public class Robot
        {
            private string name = "";   //字段:Field
            public string Name          //属性:Property,对Field进行封装。
              {
                get { return name; }
                set { name = value; }
            }
        }

    2.Attribute:

         Attribute的目标是:为元素提供附加信息。它的作用更类似于注释。

    可以说,Property/Field和Attribute是两个完全不同的概念,虽然他们有些时候能做一样的事,但请记住,他们是从本质上就不同的两个东西。

    三.实现自己的Attribute时需要注意的一些问题

    1.自定义的Attribute必须直接或者间接继承System.Attribute。

    2.这里有一个约定:所有自定义的特性名称都应该有个Attribute后缀。因为当你的Attribute施加到一个程序的元素上的时候,编译器先查找你的Attribute的定义,如果没有找到,那么它就会查找“Attribute名称"+Attribute的定义。如果都没有找到,那么编译器就报错。这就是为什么我可以再上面第一段代码中,定义一个VersionAttribute,但在第二段代码中,我使用却是Version这个Attribute。:>

    下面是一些开发自定义Attribute时,可能需要用到的资料:

    【1】Attribute可以关联的元素包括:

          程序集(assembly)、模块(module)、类型(type)、属性(property)、事件(event)、字段(field)、方法(method)、参数(param)、返回值(return)。例如

    [assembly: Version(Name = "hyddd", Date = "2009-07-20", Describtion = "hyddd's class")]
        public class MyCode
        {
            //......
        }

    Attribute 进阶用法:

    1、什么是Atrribute 
    首先,我们肯定Attribute是一个类,下面是msdn文档对它的描述:
    公共语言运行时允许你添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。

    在.NET中,Attribute被用来处理多种问题,比如序列化、程序的安全特征、防止即时编译器对程序代码进行优化从而代码容易调试等等。下面,我们先来看几个在.NET中标准的属性的使用,稍后我们再回过头来讨论Attribute这个类本身。(文中的代码使用C#编写,但同样适用所有基于.NET的所有语言)

    2、Attribute作为编译器的指令
    在C#中存在着一定数量的编译器指令,如:#define DEBUG, #undefine DEBUG, #if等。这些指令专属于C#,而且在数量上是固定的。而Attribute用作编译器指令则不受数量限制。比如下面的三个Attribute:

       Conditional:起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。 
       DllImport:用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。 
       Obsolete:这个属性用来标记当前的方法已经被废弃,不再使用了。
    下面的代码演示了上述三个属性的使用:

    1#define DEBUG //这里定义条件
       
     3using System;
     4using System.Runtime.InteropServices;
     5using System.Diagnostics;
       
     7namespace AttributeDemo
     8{
      class MainProgramClass
      {
    
         [DllImport("User32.dll")]
         public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
        
         static void Main(string[] args)
         {
            DisplayRunningMessage();
            DisplayDebugMessage();
        
            MessageBox(0,"Hello","Message",0);
        
            Console.ReadLine();
         }
        
         [Conditional("DEBUG")]
         private static void DisplayRunningMessage()
         {
            Console.WriteLine("开始运行Main子程序。当前时间是"+DateTime.Now);
         }
     
         [Conditional("DEBUG")]
         //[Obsolete("Don't use Old method, use New method", true)] 
         [Obsolete]
         private static void DisplayDebugMessage()
         {
            Console.WriteLine("开始Main子程序");
         }
      }
    39}

    如果在一个程序元素前面声明一个Attribute,那么就表示这个Attribute被施加到该元素上,前面的代码,[DllImport]施加到MessageBox函数上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。

    根据上面涉及到的三个Attribute的说明,我们可以猜到程序运行的时候产生的输出:DllImport Attribute表明了MessageBox是User32.DLL中的函数,这样我们就可以像内部方法一样调用这个函数。

    重要的一点就是Attribute就是一个类,所以DllImport也是一个类,Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。Attribute实例化的时候根据该Attribute类的设计可以带参数,也可以不带参数,比如DllImport就带有"User32.dll"的参数。Conditional对满足参数的定义条件的代码进行编译,如果没有定义DEBUG,那么该方法将不被编译,读者可以把#define DEBUG一行注释掉看看输出的结果(release版本,在Debug版本中Conditional的debug总是成立的)。Obsolete表明了DispalyDebugMessage方法已经过时了,它有一个更好的方法来代替它,当我们的程序调用一个声明了Obsolete的方法时,那么编译器会给出信息,Obsolete还有其他两个重载的版本。大家可以参考msdn中关于的ObsoleteAttribute 类的描述。

    3、Attribute类
    除了.NET提供的那些Attribute派生类之外,我们可以自定义我们自己的Attribute,所有自定义的Attribute必须从Attribute类派生。现在我们来看一下Attribute 类的细节:

    protected Attribute(): 保护的构造器,只能被Attribute的派生类调用。

    三个静态方法:

    static Attribute GetCustomAttribute():这个方法有8种重载的版本,它被用来取出施加在类成员上指定类型的Attribute。

    static Attribute[] GetCustomAttributes(): 这个方法有16种重载版本,用来取出施加在类成员上指定类型的Attribute数组。

    static bool IsDefined():由八种重载版本,看是否指定类型的定制attribute被施加到类的成员上面。

    实例方法:

    bool IsDefaultAttribute(): 如果Attribute的值是默认的值,那么返回true。

    bool Match():表明这个Attribute实例是否等于一个指定的对象。

    公共属性: TypeId: 得到一个唯一的标识,这个标识被用来区分同一个Attribute的不同实例。

    我们简单地介绍了Attribute类的方法和属性,还有一些是从object继承来的。这里就不列出来了。

    下面介绍如何自定义一个Attribute: 自定义一个Attribute并不需要特别的知识,其实就和编写一个类差不多。自定义的Attribute必须直接或者间接地从Attribute这个类派生,如:

    public MyCustomAttribute : Attribute { ... }

    这里需要指出的是Attribute的命名规范,也就是你的Attribute的类名+"Attribute",当你的Attribute施加到一个程序的元素上的时候,编译器先查找你的Attribute的定义,如果没有找到,那么它就会查找“Attribute名称"+Attribute的定义。如果都没有找到,那么编译器就报错。

    4、定义或控制特性的使用

    对于一个自定义的Attribute,你可以通过AttributeUsage的Attribute来限定你的Attribute 所施加的元素的类型。代码形式如下:
    [AttriubteUsage(参数设置)] public 自定义Attribute : Attribute { ... }

     非常有意思的是,AttributeUsage本身也是一个Attribute,这是专门施加在Attribute类的Attribute. AttributeUsage自然也是从Attribute派生,它有一个带参数的构造器,这个参数是AttributeTargets的枚举类型。下面是AttributeTargets 的定义:

    1public enum AttributeTargets
     2{
      All=16383,
      Assembly=1,
      Module=2,
      Class=4,
      Struct=8,
      Enum=16,
      Constructor=32,
      Method=64,
      Property=128,
      Field=256,
      Event=512,
      Interface=1024,
      Parameter=2048,
      Delegate=4096,
      ReturnValue=8192
    18}

    作为参数的AttributeTarges的值允许通过“或”操作来进行多个值得组合,如果你没有指定参数,那么默认参数就是All 。 AttributeUsage除了继承Attribute 的方法和属性之外,还定义了以下三个属性:

    AllowMultiple: 读取或者设置这个属性,表示是否可以对一个程序元素施加多个Attribute 。

    Inherited:读取或者设置这个属性,表示是否施加的Attribute 可以被派生类继承或者重载。

    ValidOn: 读取或者设置这个属性,指明Attribute 可以被施加的元素的类型。

    下面让我们来做一些实际的东西。我们将会在Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。

    using System; 
     [AttributeUsage(AttributeTargets.Class), AllowMultiple = false, 
     Inherited = false ] 
     public class HelpAttribute : Attribute 
     { 
     public HelpAttribute(String Description_in) 
     { 
     this.description = Description_in; 
     } 
    protected String description; 
    public String Description 
    { 
    get 
    { 
    return this.description; 
    } 
    } 
    }

    先让我们来看一下AttributeTargets.Class。它规定了Help特性只能被放在class的前面。这也就意味着下面的代码将会产生错误:

    [Help("this is a do-nothing class")] 
    public class AnyClass 
    { 
    [Help("this is a do-nothing method")] //error 
    public void AnyMethod() 
    { 
    } 
    }

    编译器报告错误如下:

      AnyClass.cs: Attribute 'Help' is not valid on this declaration type.

      It is valid on 'class' declarations only.

      我们可以使用AttributeTargets.All来允许Help特性被放置在任何程序实体前。可能的值是:

      Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,
    Parameter,Delegate。

      All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate,

      ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface )

      下面考虑一下AllowMultiple = false。它规定了特性不能被重复放置多次。

    [Help("this is a do-nothing class")] 
    [Help("it contains a do-nothing method")] 
    public class AnyClass 
    { 
    [Help("this is a do-nothing method")] //error 
    public void AnyMethod() 
    { 
    } 
    }
    

      

    它产生了一个编译期错误。

      AnyClass.cs: Duplicate 'Help' attribute

      Ok,现在我们来讨论一下最后的这个属性。Inherited, 表明当特性被放置在一个基类上时,它能否被派生类所继承。

    [Help("BaseClass")] 
    public class Base 
    { 
    } 
    public class Derive : Base 
    { 
    }

    这里会有四种可能的组合:

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ]
    
      [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ]
    
      [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ]
    
      [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ]

    第一种情况:

      如果我们查询(Query)(稍后我们会看到如何在运行期查询一个类的特性)Derive类,我们将会发现Help特性并不存在,因为inherited属性被设置为false。

      第二种情况:

      和第一种情况相同,因为inherited也被设置为false。

      第三种情况:

      为了解释第三种和第四种情况,我们先来给派生类添加点代码:

    [Help("BaseClass")] 
    public class Base 
    { 
    } 
    [Help("DeriveClass")] 
    public class Derive : Base 
    { 
    }

    现在我们来查询一下Help特性,我们只能得到派生类的属性,因为inherited被设置为true,但是AllowMultiple却被设置为false。因此基类的Help特性被派生类Help特性覆盖了。

      第四种情况:

      在这里,我们将会发现派生类既有基类的Help特性,也有自己的Help特性,因为AllowMultiple被设置为true。

    至此,我们介绍了有关Attribute类和它们的代码格式。你一定想知道到底如何在你的应用程序中使用Attribute,如果仅仅是前面介绍的内容,还是不足以说明Attribute有什么实用价值的话,那么从后面的章节开始我们将介绍几个Attribute的不同用法,相信你一定会对Attribute有一个新的了解。

    参考文章:

    http://www.cnblogs.com/hyddd/archive/2009/07/20/1526777.html

    http://www.cnblogs.com/luckboy/archive/2009/07/18/1526083.html

  • 相关阅读:
    spring-base.xml
    计算和证明施密特正交,写的很清楚
    推理
    存在某种关系时,推理存在新关系
    PyCharm 技巧
    3#记录
    2#记录
    一文揭秘!自底向上构建知识图谱全过程
    1#记录
    本体建模小结
  • 原文地址:https://www.cnblogs.com/x-poior/p/5514323.html
Copyright © 2011-2022 走看看