<NET CLR via c# 第4版>笔记 第18章 定制特性

18.1 使用定制特性

  • FCL 中的几个常用定制特性.

    • DllImport 特性应用于方法,告诉 CLR 该方法的实现位于指定 DLL 的非托管代码中.
    • Serializable 特性应用于类型,告诉序列化格式化器一个实例的字段可以序列化和反序列化.
    • AssemblyVersion 特性应用于程序集,设置程序集的版本号.
    • Flags特性应用于枚举类型,枚举类型就成了位标志集合.
  • C# 允许用一个前缀明确指定特性要应用于的目标元素.有时可省略,编译器能推断;有时则必须指定前缀.

using System;

[assembly: SomeAttr]                    //应用于程序集
[module: SomeAttr]                      //应用于模块

[type: SomeAttr]                        //应用于类型
internal sealed class SomeType<[typevar: SomeAttr] T> //应用于泛型类型变量
{
    [field: SomeAttr]                   //应用于字段
    public int SomeField = 0;

    [return: SomeAttr]                  //应用于返回值
    [method: SomeAttr]                  //应用于方法
    public int SomeMethod(
        [param: SomeAttr]               //应用于参数
        int someParam) { return someParam; }

    [property: SomeAttr]                //应用于属性
    public string SomeProp {
        [method: SomeAttr]              //应用于get访问器方法
        get { return null; }
    }

    [event: SomeAttr]                   //应用于事件
    [field: SomeAttr]                   //应用于编译器生成的字段
    [method: SomeAttr]                  //应用于编译器生成的add & remove方法
    public event EventHandler SomeEvent;
}
  • CLS 要求定制特性类必须直接或间接从公共抽象类 System.Attribute 派生.
  • 应用特性时,c# 编译器允许省略 Attribute 后缀以减少打字量,并提升源代码的可读性.
  • 特性是类的实例,语法类似于调用类的某个实例构造器.如:
    [DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true)]
  • "Kernel32" 是构造器的参数,称为定位参数,必须指定.
  • CharSetSetLastError 用于设置字段或属性,称为命名参数,是可选的.
  • 在C#中,既可将每个特性都封闭到一对方括号中,也可在一对方括号中封闭多个以逗号分隔的特性.如果特性类的构造器不获取参数,那么圆括号也可以省略,如:
    • [Serializable][Flags]
    • [Serializable,Flags]
    • [FlagsAttribute,SerializableAttribute]
    • [FlagsAttribute(), SerializableAttribute()]

18.2 定义自己的特性类

    1. Attribute 继承; 2) 类名有 Attribute 后缀(非必须).
  • AttributeUsage 特性是一个简单的类,可利用它告诉编译器定制特性的合法应用范围.所有编译器都内建了对该特性的支持.如:
    [AttributeUsage(AttributeTargets.Enum, Inherited = false)]
    public class FlagsAttribute : Attribute
    {
        public FlagsAttribute() { }
    }
  • AttributeUsageAttribute 类有一个公共构造器,它允许传递位标志来指明特性的合法应用范围.
  • AttributeUsageAttribute 类有两个公共属性.其中 AllowMultiple 指示是否允许将该特性多次应用于同一个目标元素; Inherited 指出特性在应用于基类时,是否同时应用于派生类和重写的方法.
  • 如果忘记向自己的特性类应用 AttributeUsageAttribute 特性,则特性类默认为应用于所有目标元素,向每个目标元素都只能应用一次,而且可继承.

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

  • 定义特性类的实例构造器字段和属性时,只允许Boolean,Char,Byte,SByte,Int16,UInt16,Int32,UInt32,Int64,UInt64,Single,Double,String,Type,Object 或枚举类型.
  • 应用特性时必须传递一个编译时常量表达式,它与特性类定义的类型匹配.
  • 定制特性: 它是类的实例,被序列化成驻留在元数据中的字节流.远行时可对元数据中的字节进行反序列化,从而构造出类的实例.

18.4 检测定制特性

  • 有三个方法可以获取与目标关联的特性: IsDefined,GetCustomAttributesGetCustomAttribute
  • IsDefined 比另外两个方法更高效,因为 IsDefined 不会构造特性对象,不会调用构造器,也不会设置字段和属性.

18.5 两个特性实例的相互匹配

  • System.Attribute 重写了 ObjectEquals 方法,利用反射来比较两个特性对象中的字段值(为每个字段都调用 Equals).所有字段都匹配就返回 true ; 否则返回 false.建议重写 Equals 来移除反射的使用.
  • System.Attribute 还公开了虚方法 Match ,其默认实现只是调用 Equals 方法并返回它的结果.

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

18.7 条件特性类

原文地址:https://www.cnblogs.com/harry-wang/p/7403371.html