zoukankan      html  css  js  c++  java
  • C# Attribute 特性 学习

    C# Attribute 特性 学习

    一、特性的概述
    1. 公共语言运行库允许您添加类似关键字的描述性声明(称为特性 (Attribute))来批注编程元素,如类型、字段、方法和属性 (Property)。属性与 Microsoft .NET Framework 文件的元数据一起保存,并且可用于向运行库描述代码或影响应用程序的运行时行为。
    2. 特性是一个对象,它可以加载到程序集及程序集的对象中,这些对象包括 程序集本身、模块、类、接口、结构、构造函数、方法、方法参,方法的返回值等,加载了特性的对象称作特性的目标。特性是为程序添加元数据(描述数据的数据)的一种机制,通过它可以给编译器提供指示或者提供对数据的说明。
    3. 为运行库编译代码时,该代码被转换为 Microsoft 中间语言 (MSIL),并同编译器生成的元数据一起被放到可移植可执行 (PE) 文件的内部。属性 (Attribute) 使您得以向元数据中放置额外的描述性信息,并可使用运行库反射服务提取该信息。
    4. 特性是一个对象,它是一个直接或间接继承System.Attribute的类的对象,只是特性类的实例化和应用方式有点特别,下面会详细说来!
    二、应用特性
    1. 通过从 .NET Framework 导入属性的命名空间来定义新的属性或使用现有属性。 例如:
                [Obsolete("请使用新的SendMsg(Message msg)重载方法")] 
                public static void ShowMsg() { 
                       Console.WriteLine("这是旧的SendMsg()方法"); 
                } 
      Obsolete特性是一个系统特性,标记不再使用的程序元素。
    2. 通过在紧邻代码元素之前放置属性来将该属性应用于代码元素。 特性的使用方法:首先是有一对方括号“[]”,在左方括号“[”后紧跟特性的名称,比如Obsolete,随后是一个圆括号“()”。 和普通的类不同,这个圆括号不光可以写入构造函数的参数,还可以给类的属性赋值,在Obsolete的例子中,仅传递了构造函数参数。 当特性应用与程序集和模块时,C#允许以指定的前缀来表示特性所应用的目标元素,建议这样来处理,因为显式处理可以消除可能带来的二义性。 例如:
      namespace Anytao.net  { 
          [assembly: MyAttribute(1)]          //应用于程序集
          [moduel: MyAttribute(2)]            //应用于模块
          pubic class Attribute_how2do     {         //略     } 
      }
    3. 为属性指定位置参数和命名参数。
      位置参数:构造函数的参数。 命名参数:属性作为参数。 使用构造函数参数,参数的顺序必须同构造函数声明时的顺序相同,所有在特性中也叫位置参数(Positional Parameters),与此相应,属性参数也叫做命名参数(Named Parameters)。位置参数是必需的,并且必须放在所有命名参数之前;
    三编写自定义
    1. 创建自定义类,类名以Attribute结尾(所有特性类都是以Attribute结尾,这是约定俗成的,这样符合微软的命名风格,也符合编译器的搜索规则,但不是必须的),间接或者直接继承System.Attribute类。
    2. 在我们的特性类上应用AttributeUsage系统特性,用于指定我们的自定义特性的应用目标类型。AttributeUsage特性的位置参数有一个是AttributeTargets枚举位标记类型的,通过它来指定我们的自定义特性的应用目标。另外还有两个命名参数 AllowMultiple 是否可以将特性重复应用到目标上;Inherited 咱们自定义的特性类是否可以被继承。
    3. 声明类属性,用于存储我们要添加的元数据。通过构造函数赋值的属性,设为只读(只有get访问器),其它则为读写属性。这里这样做的目的是,在特性使用的时候,构造函数参数也就是位置参数是必需提供的,而命名参数,也就是为读写属性赋值是可选的,你将如果将位置参数设为读写属性,那它也将出现在命名参数中。
    4. 声明构造函数,为只读属性赋值。当然你也可以在里面声明一些方法,只是方法在应用特性的时候没法体现。也可以像普通自定义类一样,调用类的方法。
    四、自定义特性范例
    假设我们有这样一个很常见的需求:我们在创建或者更新一个类文件时,需要说明这个类是什么时候、由谁创建的,在以后的更新中还要说明在什么时候由谁更新的,可以记录也可以不记录更新的内容,以往你会怎么做呢?是不是像这样在类的上面给类添加注释:
    //更新:Joey, 2013-07-23
    //更新:Joey, 2013-07-23,修改bug
    //创建:zoe, 2013-07-15
    public class DemoClass{
      // Class Body
    }
    这样的的确确是可以记录下来,但是如果有一天我们想将这些记录保存到数据库中作以备份呢?你是不是要一个一个地去查看源文件,找出这些注释,再一条条插入数据库中呢? 通过上面特性的定义,我们知道特性可以用于给类型添加元数据,这些元数据可以用于描述类型。那么在此处,特性应该会派上用场。那么在本例中,元数据应该是:注释类型(“更新”或者“创建”),修改人,日期,备注信息(可有可无)。实现代码如下:
     View Code

    namespace Common.Library.ChangeRecord
    {
    /// <summary>
    /// desc:记录 修改记录 的特性
    /// </summary>
    [AttributeUsage((AttributeTargets)6140, AllowMultiple = true, Inherited = false)]
    public class RecordAttribute : Attribute
    {
    private RecordType recordType; // 记录类型:更新/创建
    private string author; // 作者
    private DateTime date; // 更新/创建 日期
    private string mark; // 备注

    // 构造函数,构造函数的参数在特性中也称为“位置参数”。
    public RecordAttribute(RecordType recordType, string author, string date)
    {
    this.recordType = recordType;
    this.author = author;
    this.date = Convert.ToDateTime(date);
    }

    // 对于位置参数,通常只提供get访问器
    public RecordType RecordType { get { return recordType; } }
    public string Author { get { return author; } }
    public DateTime Date { get { return date; } }

    // 构建一个属性,在特性中也叫“命名参数”
    public string Mark
    {
    get { return mark; }
    set { mark = value; }
    }
    }
    /// <summary>
    /// 记录类型 枚举
    /// </summary>
    public enum RecordType
    {
    Add,
    Modify
    }
    }

     
    NOTE:注意构造函数的参数 date,必须为一个常量、Type类型、或者是常量数组,所以不能直接传递DateTime类型。
    这个类看上去和普通的类没有和大区别,显然不能它因为名字后面跟了个Attribute就摇身一变成了特性。它满足了,我们前面所说的自定义特性的要求,继承了System.Attribute类、用AttributeUsage特性限制了应用的目标、有属性和构造函数等。
    
    下面是应用特性:
     View Code

    namespace Common.Library
    {
    [Record(RecordType.Add, "Joey", "2013-07-23")]
    [Record(RecordType.Modify, "Joey", "2013-07-23", Mark = "测试")]
    [Record(RecordType.Modify, "Joey", "2013-07-24", Mark = "修改bug")]
    public class TestAttribute
    {
    [Record(RecordType.Add, "Joey", "2013-07-23", Mark = "测试特性附加到方法上")]
    [Record(RecordType.Add, "Shovy", "2013-07-25", Mark = "修改此方法")]
    public void MyMethod()
    {
    }

    [Record(RecordType.Add, "Joey", "2013-07-23", Mark = "方法2")]
    [Record(RecordType.Add, "Zoe", "2013-07-25", Mark = "修改方法2")]
    public void Method2()
    {
    }
    }
    }

     
    这样一个自定义特性就写好了,也应用到目标元素上了。前面我们说了特性是给目标类型添加描述性的元数据,它不会影响代码的运行。上面代码编译后,我们特性所添加的信息已经作为元数据添加到了程序集中。可以通过IL DASM看到:
     
    五、使用反射查看自定义特性

    特性一般是和反射结合使用的,你只附加了特性你不去获取它,也没有任何意义。而获取类型上的特性信息,是通过反射来实现的。在这之前我们有必要先了解下System.Attribute这个特性基类。
    System.Attribute:
    1. protected Attribute(): 保护的构造器,只能被Attribute的派生类调用。
    2. 三个静态方法: static Attribute GetCustomAttribute():这个方法有8种重载的版本,它被用来取出施加在类成员上指定类型的Attribute。 static Attribute[] GetCustomAttributes(): 这个方法有16种重载版本,用来取出施加在类成员上指定类型的Attribute数组。 static bool IsDefined():由八种重载版本,看是否指定类型的定制attribute被施加到类的成员上面。
    3. 实例方法: bool IsDefaultAttribute(): 如果Attribute的值是默认的值,那么返回true。 bool Match():表明这个Attribute实例是否等于一个指定的对象。
    4. 公共属性: TypeId: 得到一个唯一的标识,这个标识被用来区分同一个Attribute的不同实例。 我们简单地介绍了Attribute类的方法和属性,还有一些是从object继承来的。这里就不列出来了。
    下面的代码是检测上面我们自定义的特性的应用:
    
    
     View Code

    namespace ConsoleApp
    {
    class Program
    {
    static void Main(string[] args)
    {
    TestAttribute testClass = new TestAttribute();
    Type type = testClass.GetType(); //获取要获得自定义特性的类型的Type对象
    Attribute[] myAttributes = Attribute.GetCustomAttributes(type);//获取类型上添加的自定义特性
    Console.WriteLine("类型{0}上应用的Record特性:", type.ToString());
    Console.WriteLine();
    foreach (Attribute item in myAttributes)
    {
    if (item.GetType() == typeof(RecordAttribute)) //只打印咱自定义的特性
    {
    RecordAttribute attr = (RecordAttribute)item;
    Console.WriteLine(" 类型:{0},更改人:{1},更改时间:{2},备注:{3}", attr.RecordType, attr.Author, attr.Date.ToString("yyyy-MM-dd"), attr.Mark);
    }
    }
    Console.WriteLine();
    Console.WriteLine("类型{0}的方法上应用的Record特性:", type.ToString());
    Console.WriteLine();
    foreach (MethodInfo mInfo in type.GetMethods()) //遍历该类型的所有方法
    {
    if (Attribute.IsDefined(mInfo, typeof(RecordAttribute))) //只有在方法上附加了属性,才遍历
    {
    Console.WriteLine(" 类型{0}的{1}方法上的Record特性", type.ToString(), mInfo.ToString());
    Console.WriteLine();
    foreach (Attribute item in Attribute.GetCustomAttributes(mInfo)) //遍历方法上的 特性
    {
    if (item.GetType() == typeof(RecordAttribute))
    {
    RecordAttribute attr = (RecordAttribute)item;
    Console.WriteLine(" 类型:{0},更改人:{1},更改时间:{2},备注:{3}", attr.RecordType, attr.Author, attr.Date.ToString("yyyy-MM-dd"), attr.Mark);
    }
    }
    Console.WriteLine();
    }
    }
    }
    }}

     
    运行效果如下图所示:

    获取自定义特性的方法二:
    首先基于类型(本例中是DemoClass)获取一个Type对象,然后调用Type对象的GetCustomAttributes()方法,获取应用于该类型上的特性。
    当指定GetCustomAttributes(Type attributeType, bool inherit) 中的第一个参数attributeType时,
    将只返回指定类型的特性,否则将返回全部特性;第二个参数指定是否搜索该成员的继承链以查找这些属性。
    代码如下:
     View Code

    namespace ConsoleApp
    {
    class Program
    {
    static void Main(string[] args)
    {
    TestAttribute testClass = new TestAttribute();
    Type type = testClass.GetType();
    object[] records = type.GetCustomAttributes(typeof(RecordAttribute), false); //获取类型上的特性
    Console.WriteLine("类型{0}上应用的Record特性:", type.ToString());
    Console.WriteLine();
    foreach (RecordAttribute record in records)
    {
    Console.WriteLine(" 类型:{0},更改人:{1},更改时间:{2},备注:{3}", record.RecordType, record.Author, record.Date.ToString("yyyy-MM-dd"), record.Mark);
    }
    Console.WriteLine();
    Console.WriteLine("类型{0}的方法上应用的Record特性:", type.ToString());
    Console.WriteLine();
    foreach (MethodInfo mInfo in type.GetMethods()) //遍历该类型的所有方法
    {
    if (Attribute.IsDefined(mInfo, typeof(RecordAttribute))) //只有在方法上附加了属性,才遍历
    {
    Console.WriteLine(" 类型{0}的{1}方法上的Record特性", type.ToString(), mInfo.ToString());
    Console.WriteLine();
    foreach (RecordAttribute record in mInfo.GetCustomAttributes(typeof(RecordAttribute), false)) //遍历方法上的 特性
    {
    Console.WriteLine(" 类型:{0},更改人:{1},更改时间:{2},备注:{3}", record.RecordType, record.Author, record.Date.ToString("yyyy-MM-dd"), record.Mark);
    }
    Console.WriteLine();
    }
    }
    }
    }
    }

     
    两种方法运行的结果是一样的,如上图。这两种方法的区别在与,第一种方法是用的Atrribute基类的静态方法获取对应类型的自定义特性的集合。而第二种方案是使用的Type类型的方法获取的。
    demo下载:点击这里下载代码
    资料:

    http://www.cnblogs.com/JimmyZhang/archive/2008/01/27/1055254.html

    Attribute在.net编程中的应用(一)
    Attribute在.net编程中的应用(二)
    Attribute在.net编程中的应用(三)
    Attribute在.net编程中的应用(四)
    Attribute在.net编程中的应用(五)
     
     
    分类: C#C# 2.0
  • 相关阅读:
    PHP---无限极分类数组处理
    PHPExcel数据导入(含图片)
    PHP数组与xml互相转换
    微信APP支付【签名失败】
    winform窗体关闭方案
    ss的优先级 和 权重
    Anaconda 与 conda 区别
    c#FileStream文件读写
    C# DataTable 某一列取算
    关于解决DevExpress用DevExpress patch工具破解后经常弹出试用框的问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3209664.html
Copyright © 2011-2022 走看看