zoukankan      html  css  js  c++  java
  • 定制特性

    定制特性

    • 定制特性 可宣告式的为自己的代码构造添加注解来实现特殊功能。

    • 定制特性允许为每一个元数据记录项定义和应用信息。

    • 这种可扩展的元数据信息能在运行时查询,从而动态改变代码的执行方式。

    先看一个应用了特性的例子:

    [Custom("这里是学生", Description = "123456", Remark = "123456")]
    public class Student
    {
      [Custom] //使用特性时可以省略Attribute
       public Student()
      {
      Console.WriteLine("这里是student");
      }

      [Custom]
       public int Id { get; set; }
    }

       

    这里是定义特性的类,它继承Attribute。

    [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] //参数AttributeTargets -- 目标,AllowMultiple -- 允许多重修饰, Inherited -- 允许继承
    public class CustomAttribute : Attribute
    {
       public CustomAttribute()
      {
      Console.WriteLine("这里是CustomAttribute");
      }
       public CustomAttribute(string remark)
      {
      Console.WriteLine("这里是CustomAttribute带参数");
      }

    public string Remark { get; set; }

    public string Description { get; set; }

       public void Show()
      {
           Console.WriteLine($"This is {this.GetType().Name}");
      }

    }

    public class CustomChildAttribut : CustomAttribute
    {
    }

    上述例子中 对于自己定义的特性也应用了一个特性,也为特性本身就是类。AttributeUsage 特性是一个简单的类,可利用它告诉编译器定制特性的合法应用范围。编译器都内建了对该特性的支持,并会在定制特性应用于无效目标时报错。

    The System.AttributeTargets 枚举类型在FCL 中是像下面这样定义的。

    [Flags, Serializable]
    public enum AttributeTargets {
       Assembly = 0x0001,
       Module = 0x0002,
       Class = 0x0004,
       Struct = 0x0008,
       Enum = 0x0010,
       Constructor = 0x0020,
       Method = 0x0040,
       Property = 0x0080,
       Field = 0x0100,
       Event = 0x0200,
       Interface = 0x0400,
       Parameter = 0x0800,
       Delegate = 0x1000,
       ReturnValue = 0x2000,
       GenericParameter = 0x4000,
       All = Assembly | Module | Class | Struct | Enum |
           Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate | ReturnValue | GenericParameter
    }

    特性到底是什么?

    • 定制特性其实是一个类型的实例。为符合CLS ,定制特性类必须直接或间接从公共抽象类 system.Attribute 派生。

    • 特性是类的实例,类必须有公共构造器才能创建它的实例。所以,将特性应用于目标元素时,语法类似于调用类的某个实例构造器。

     

    特性构造器和字段/属性数据类型*

    特性本质是一个类,那它就有自己的构造函数,字段/属性。在定义特性类的实例构造器、字段和属性时,可供选择的数据类型不多,只允许以下类型:

    Boolean, Char, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, String, Type, Object

     

    检测定制特性

    仅仅定义特性类没有用,将特性应用于类、方法上也只是在程序集生成额外的元数据,也没意义,应用程序的代码不会有任何改变。

    类、方法在调用的时候让特性起到作用需要在运行时检查。检查程序执行的类实例或方法是否关联了特性元数据。代码利用一种反射的技术检测特性的存在。下面结合上述的代码举个例子:

    public static void Manage(Student student)
    {
    Type type = student.GetType();

    if (type.IsDefined(typeof(CustomAttribute), true))//检测有没有这个特性
    {
           object item = type.GetCustomAttributes(typeof(CustomAttribute), true)[0];
           foreach (var item in type.GetCustomAttributes(typeof(CustomAttribute), true))
          {
           CustomAttribute attribute = item as CustomAttribute;
           attribute.Show();
          }
      }
    }

    上述代码中 从student实例中得到类型信息,用IsDefined 方法检测student 实例有没有关联CustomAttribute 特性。如果只想判断目标是否应用了特性,有IsDefined就够了。如果要构造特性对象,必须调用GetCustomAttributes 方法和 GetCustomAttribute 方法。调用这两个方法会构造指定特性类型的新实例。

     

    调用表中任何一种方法,内部都必须扫描托管模块的元数据,执行字符串比较来定位指定的定制特性类。这肯定会消耗一定时间。假如对性能的要求比较高,可考虑缓存这些方法的调用结果。

    两个特性实例的相互匹配

    除了判断是否像目标应用了一个特性的实例,可能还需要检查特性的字段来确定它们的值。system.Attribute 重写了obejct 的 Equals方法,会在内部比较两个对象的类型。 可以在自己的定制特性类中重写Equals 来移除反射的使用,从而提升性能。system.Attribute 还公开了虚方法 Match,可重写它来提供更丰富的语义。

     

    检测定制特性时不创建从Attribute派生的对象。

    如何用另一种技术检测应用于元数据记录项的特性。这适用于比较安全的场合,这个技术保证不会调用从attribute派生的类中的代码。原因是在调用GetCustomAttribute 方法时要调用特性类的构造器。 可用system.Reflection.CustiomAttributeData类在查找特性的同时禁止执行特性类中的代码。1 先用Assembly 的静态方法 ReflectionOnlyLoad加载程序集,再调用CustomAttributeData 类分析这个程序集中的元数据中的特性。

     

    条件特性

    应用了System.Diagnostics.ConditionalAttribute 的特性类称为条件特性类。这个类可以只在符合条件的情况下编译器才生成这个特性。

    //#define TEST
    #define VERIFY
    using System;
    using System.Diagnostics;
    [Conditional("TEST")][Conditional("VERIFY")]
    public sealed class CondAttribute : Attribute {
    }
    [Cond]
    public sealed class Program {
       public static void Main() {
           Console.WriteLine("CondAttribute is {0}applied to Program type.",
       Attribute.IsDefined(typeof(Program),typeof(CondAttribute)) ? "" : "not ");
      }
    }

    编译器如果发现向目标元素应用了 CondAttribute 的实例,那么当含有目标元素的代码编译时,只有在定义 TEST 或VERIFY 符号的前提下,编译器才会在元数据中生成特性信息。不过特性类的定义元数据和实现任存在于程序集中。

  • 相关阅读:
    firstResponder
    形而上学
    du -h
    数据本地存贮与数据结构(对象)
    RFC
    oc语言源代码
    HTTP1.1协议中文版-RFC2616
    如何提高团队协作的效率
    iOS应用架构谈 开篇
    nginx版本如何选择?
  • 原文地址:https://www.cnblogs.com/mingjie-c/p/11688013.html
Copyright © 2011-2022 走看看