C#反射和特性

C#反射和特性

首先什么是元数据

元数据就是指程序和程序类型本身的的信息,保存在程序的程序集中

什么是反射

程序在运行的时候,可以查看其他程序集或者其本身的元数据。这个行为就是反射。

什么是Type类

一个抽象类,用来包含类型的特性,这个类的对象能让我们获取程序使用的类型的信息。

由于他是抽象类所以获取的是派生类实例。

1.程序每创建一个类型,CLR会创建一个包含这个类型信息的Type类派生类型的实例

2.无论一个类型创建多少个实例,这些实例都只关联一个Type实例

如何获得Type类型(实际上是获得派生类)

方法1. 通过GetType方法获取Type类型

//基类
class BaseClass
    public int BaseField = 0;
//派生类
class DerivedClass : BaseClass
    public int DerivedField = 0;
class Program
    static void Main(string[] args)
        var bc = new BaseClass();
        var dc = new DerivedClass();
        BaseClass[] bca = new BaseClass[] { bc, dc };
        foreach(var v in bca)
            //获取类型
            Type t = v.GetType();
            Console.WriteLine("Object Type: {0}", t.Name);
            //获取类中的字段
            FieldInfo[] fi = t.GetFields();
            foreach (var f in fi)
                Console.WriteLine("     Field:{0}", f.Name);
            Console.WriteLine();
        Console.WriteLine("End!");
        Console.ReadKey();
Object Type: BaseClass
     Field:BaseField
Object Type: DerivedClass
     Field:DerivedField
     Field:BaseField

方法2.通过Typeof()

Type t = typeof(DerivedClass);
//通过程序集获取类型
var baseType = Assembly.GetExecutingAssembly().GetType("TestDemo.BaseClass");
var derivedType = Assembly.GetExecutingAssembly().GetType("TestDemo.DerivedClass");

下面是一些运用实例

获取数组类型

static void Main(string[] args)
    var intArray = typeof(int).MakeArrayType();
    var int3Array = typeof(int).MakeArrayType(3);
    Console.WriteLine($"是否是int 数组 intArray == typeof(int[]) :{intArray == typeof(int[]) }");
    Console.WriteLine($"是否是int 数组 intArray3 == typeof(int[]) :{int3Array == typeof(int[]) }");
    Console.WriteLine($"是否是int 3维数组 intArray3 == typeof(int[,,]):{int3Array == typeof(int[,,]) }");
    //数组元素的类型
    Type elementType = intArray.GetElementType();
    Type elementType2 = int3Array.GetElementType();
    Console.WriteLine($"{intArray}类型元素类型:{elementType }");
    Console.WriteLine($"{int3Array}类型元素类型:{elementType2 }");
    //获取数组的维数
    var rank = int3Array.GetArrayRank();
    Console.WriteLine($"{int3Array}类型维数:{rank }");
    Console.ReadKey();
MakeArrayType() 可以用来获取数组类型,有一个参数是数组的维数
GetElementType() 可以用来获取数组元素的类型
GetArrayRank() 可以获取数组的维数

获取嵌套类型

public class Class
    public class Student
        public string Name { get; set; }
class Program
    static void Main(string[] args)
        #region 嵌套类型
        var classType = typeof(Class);
        foreach (var t in classType.GetNestedTypes())
            Console.WriteLine($"NestedType ={t}");
            //获取一个值,该值指示 System.Type 是否声明为公共类型。
            Console.WriteLine($"{t}访问 {t.IsPublic}");
            //获取一个值,通过该值指示类是否是嵌套的并且声明为公共的。
            Console.WriteLine($"{t}访问 {t.IsNestedPublic}");
        Console.ReadKey();
        #endregion
GetNestedTypes()获取嵌套类
NestedType =TestDemo.Class+Student
TestDemo.Class+Student访问 False
TestDemo.Class+Student访问 True

获取类型名称

Type里面具有NameSpace、Name和FullName属性。一般FullName是两者的组合。但是对于嵌套类型和封闭式泛型不成立

static void Main(string[] args)
    #region 获取名称
    var type = typeof(Class);
    Console.WriteLine($"\n------------一般类型-------------");
    PrintTypeName(type);
    //嵌套类型
    Console.WriteLine($"\n------------嵌套类型-------------");
    foreach (var t in type.GetNestedTypes())
        PrintTypeName(t);
    var type2 = typeof(Dictionary<,>);            //非封闭式泛型
    var type3 = typeof(Dictionary<string, int>);  //封闭式泛型
    Console.WriteLine($"\n------------非封闭式泛型-------------");
    PrintTypeName(type2);
    Console.WriteLine($"\n------------封闭式泛型-------------");
    PrintTypeName(type3);
    Console.ReadKey();
    #endregion
private static void PrintTypeName(Type t)
    Console.WriteLine($"NameSpace: {t.Namespace}");
    Console.WriteLine($"Name :{t.Name}");
    Console.WriteLine($"FullName: {t.FullName}");
------------一般类型-------------
NameSpace: TestDemo
Name :Class
FullName: TestDemo.Class
------------嵌套类型-------------
NameSpace: TestDemo
Name :Student
FullName: TestDemo.Class+Student
NameSpace: TestDemo
Name :Teacher
FullName: TestDemo.Class+Teacher
------------非封闭式泛型-------------
NameSpace: System.Collections.Generic
Name :Dictionary`2
FullName: System.Collections.Generic.Dictionary`2
------------封闭式泛型-------------
NameSpace: System.Collections.Generic
Name :Dictionary`2
FullName: System.Collections.Generic.Dictionary`2[
[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],
[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

获取基类类型和接口类型

var base1 = typeof(System.String).BaseType;
var base2 = typeof(System.IO.FileStream).BaseType;
var base3 = typeof(DerivedClass).BaseType;
Console.WriteLine($"base1 :{base1.Name}");
Console.WriteLine($"base2 :{base2.Name}");
Console.WriteLine($"base3 :{base3.Name}");
foreach (var iType in typeof(Guid).GetInterfaces())
    Console.WriteLine($"iType :{iType.Name}");
base1 :Object
base2 :Stream
base3 :BaseClass
iType :IFormattable
iType :IComparable
iType :IComparable`1
iType :IEquatable`1


我们在判断某个实例对象是否是某个类型的时候,经常使用 is语句。

Type中的方法 IsInstanceOfType 其实和is是等价的。

var baseClassObject = new BaseClass();
var check1 = baseClassObject is BaseClass;
var check2 = base3.IsInstanceOfType(baseClassObject);  //这里的base3上面有
Console.WriteLine($"使用is判断类型是否相同 :{check1}");  //结果True
Console.WriteLine($"使用IsInstanceOfType类型是否相同 :{check2 }"); //结果True 

还有一个是 IsAssignableFrom ,它的作用是 确定指定类型的实例是否可以分配给当前类型的实例。

var base4 = typeof(BaseClass); //type类
var baseClassObject = new BaseClass();
var derivedClassObject = new DerivedClass();
var classObject = new Class();
var checkResult1 = base4.IsAssignableFrom(baseClassObject.GetType()); //判断BaseClass类型是否可以分配给BassClass类型
var checkResult2 = base4.IsAssignableFrom(derivedClassObject.GetType());  //判断DerivedClass类型是否可以分配给BassClass类型
var checkResult3 = base4.IsAssignableFrom(classObject.GetType()); //判断Class类型是否可以分配给BassClass类型
Console.WriteLine($"使用IsAssignableFrom类型是否和接受的类型一致 :{checkResult1}");   //True
Console.WriteLine($"使用IsAssignableFrom类型是否和接受的类型一致 :{checkResult2}");   //True
Console.WriteLine($"使用IsAssignableFrom类型是否和接受的类型一致 :{checkResult3}");  //False

实例化类型

I. 有两种方法可以动态的实例化类型。

方法一 通过静态的 Activator.CreateInstance()方法创建,它有多个重载函数。

var dateTime1 = (DateTime)Activator.CreateInstance(typeof(DateTime),2019,6,19);
var dateTime2 = (DateTime)Activator.CreateInstance(typeof(DateTime), 2019,6,19,10,10,10);
Console.WriteLine($"DateTime1: {dateTime1}"); //DateTime1: 2019/6/19 0:00:00
Console.WriteLine($"DateTime2: {dateTime2}"); //DateTime2: 2019/6/19 10:10:10
//Activator.CreateInstance(Type,要转成的类型的构造函数中的参数)

方法二 调用ConstructInfo对象上面的Invoke方法,ConstructInfo对象是通过调用类型(高级环境)上的GetConstructor方法获取的。

先分析一下场景,例如我有下面这样的一个类型:

public class InvokeClass
    private string _testString;
    private long _testInt;
    public InvokeClass(string abc)
        _testString = abc;
    public InvokeClass(StringBuilder abc)
        _testString = abc.ToString();
   public InvokeClass(string abc,long def)
        _testString = abc;
        _testInt = def;
}

如果这时候使用

var t=(InvokeClass)Activator.CreateInstance(typeof(InvokeClass),null)
报错

由于InvokeClass有三个构造函数,使用new InvokeClass(null)存在异义性,所以上面语句报错

而如果我们使用ConstructInfo的方式就可以指定对应的构造函数了

//找到一个参数为string的构造函数
var constructorInfo = typeof(InvokeClass).GetConstructor(new[] { typeof(string)});
//使用该构造函数传入一个null参数     
var obj4 = (InvokeClass)constructorInfo.Invoke(new object[] { null });

还可以结合查询来找到对应的构造函数

//获取所有的构造函数
var constructorInfoArray = typeof(InvokeClass).GetConstructors();
//过滤一次,获取所有两个参数的构造函数
var constructorInfoArray2 = Array.FindAll(constructorInfoArray, x => x.GetParameters().Length == 2);
//最后找的第二个参数是long类型的构造函数
var constructorInfo2 = Array.Find(constructorInfoArray2, x => x.GetParameters()[1].ParameterType == typeof(long));
//如果存在,就创建对象
if (constructorInfo2 != null)
    var obj5 = (InvokeClass)constructorInfo2.Invoke(new object[] { "abc", 123 });
}

动态构造对象的缺点就是慢,简单对比一下,采用反射和new创建100万个对象,耗时对比还是比较明显的。

var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100000; i++)
    var obj3 = (InvokeClass)Activator.CreateInstance(typeof(InvokeClass), "abc", 123);
sw.Stop();
Console.WriteLine($"时间:{sw.ElapsedMilliseconds}ms");
var sw2 = new Stopwatch();
sw2.Start();
for (int i = 0; i < 100000; i++)
    var obj = new InvokeClass("abc", 123);
sw2.Stop();
Console.WriteLine($"时间:{sw2.ElapsedMilliseconds}ms");
时间:280ms
时间:1ms

II. 实例化委托

动态创建静态方法和实例方法的委托传入的参数不太一样,使用的是CreateDelegate的重载,可以参考下面的例子

/// <summary>
///  创建指定类型的委托,该委托表示要对指定的类实例调用的指定实例方法。
/// </summary>
/// <param name="type">要创建的委托的 System.Type</param>
/// <param name="target"> 类实例,对其调用 method</param>
/// <param name="method">委托要表示的实例方法的名称</param>
/// <returns></returns>
public static Delegate CreateDelegate(Type type, object target, string method);
/// <summary>
///  创建指定类型的委托,该委托表示指定类的指定静态方法。
/// </summary>
/// <param name="type">要创建的委托的 System.Type</param>
/// <param name="target">  表示实现 method 的类的 System.Type</param>
/// <param name="method"> 委托要表示的静态方法的名称。</param>
/// <returns></returns>
public static Delegate CreateDelegate(Type type, Type target, string method);
class Program
    public static int StaticSum(int a, int b)   {
        return a + b;
    public int InstanceSum(int a, int b)
        return a + b;
    //创建一个委托
    delegate int delegateOperate(int a, int b);
    static void Main(string[] args)
        #region 实例化委托
        //静态方法的委托
        //返回一个委托类型,并且是静态的,因为下面是typeof(Program)
        Delegate staticD = Delegate.CreateDelegate(typeof(delegateOperate), typeof(Program), "StaticSum");
        //实例方法的委托
        //返回一个委托类型,委托包含一个Program类实例(new Program())的实例方法
        Delegate instanceD = Delegate.CreateDelegate(typeof(delegateOperate), new Program(), "InstanceSum");
        Console.WriteLine($"staticD:{staticD.DynamicInvoke(1,2)}");
        Console.WriteLine($"instanceD:{instanceD.DynamicInvoke(10,20)}");
        #endregion
        Console.ReadKey();
staticD:3
instanceD:30

III.范型的实例化
泛型分为封闭型和未封闭型,对于封闭类型的泛型是可以通过反射进行实例化的,而未封闭的泛型不能实例化

封闭式的泛型和未绑定的泛型是可以相互转换的。

①未绑定的泛型可以通过 MakeGenericType 变成封闭的

②封闭的可以通过GetGenericTypeDefinition 获取未绑定的类型。

class Program
    static void Main(string[] args)
        Type closed = typeof(List<int>);
        Type unBound = typeof(List<>);
        var newClosed = unBound.MakeGenericType(typeof(int));
        var newUnBound = closed.GetGenericTypeDefinition();
        Console.WriteLine($"List<int> 类型{closed}");
        Console.WriteLine($"List<> 类型{unBound}");
        Console.WriteLine($"List<> MakeGenericType执行后 类型{newClosed}");
        Console.WriteLine($"List<int> GetGenericTypeDefinition执行后 类型{newUnBound}");
List<int> 类型System.Collections.Generic.List`1[System.Int32]
List<> 类型System.Collections.Generic.List`1[T]
List<> MakeGenericType执行后 类型System.Collections.Generic.List`1[System.Int32]
List<int> GetGenericTypeDefinition执行后 类型System.Collections.Generic.List`1[T]

C#特性

什么是特性

特性是一种允许我们向程序的程序集添加元数据的语言结构,它是用于保存程序结构信息的某种特殊类型的类。

注释是对程序源代码的一种说明,主要目的是给人看的,在程序被编译的时候会被编译器所丢弃,因此,它丝毫不会影响到程序的执行。

Attribute是程序代码的一部分,它不但不会被编译器丢弃,而且还会被编译器编译进程序集(Assembly)的元数据(Metadata)里。

//先定义一个人类Person类
[AnimalAttribute(IsPrimate=true)]
        class Person
            //人的姓名储存字段和属性 
            private string name;
            public string Name
                set { name = value; }
                get { return name; }
            //人的年龄储存字段和属性 
            private int age;
            public int Age
                set { age = value; }
                get { return age; }
            //人的性别储存字段和属性 
            private char sex;
            public char Sex
                set { sex = value; }
                get { return sex; }
            //人的打招呼方法 
            public void SayHello()
                Console.WriteLine($"大家好,我叫{this.Name},我今年{this.Age}岁了,我的性别是{this.Sex}");
        //定义动物的特性类AnimalAttribute类继承于Attribute(特性)
        class AnimalAttribute : Attribute
            //字段和属性描述是否是灵长类
            private bool isPrimate;
            public bool IsPrimate
                set { isPrimate = value; }
                get { return isPrimate; }
//声明特性对象,并通过Attribute类的静态方法GetCustomAttribute()获得人类的在动物类的特性,并赋值给特性对象 
            Attribute att1 = Attribute.GetCustomAttribute(typeof(Person), typeof(AnimalAttribute));
            //将特性对象转化为动物特性对象
            AnimalAttribute animalAtt = att1 as AnimalAttribute;
            //检查转化是否成功如果成功则打印这个特性对象的是否是灵长类的属性。
            if (animalAtt != null)
                Console.WriteLine("人类是否是灵长类:{0}", animalAtt.IsPrimate);
            Console.ReadKey();

特性的使用方法

//长记法
[ConditionalAttribute("Li")]
[ConditionalAttribute("NoBug")]
public static void Func()
{Console.WriteLine("Created by Li, NoBug"); }
//短记法
[Conditional("Li")]
[Conditional("NoBug")]
public static void Func()
{Console.WriteLine("Created by Li, NoBug"); }
[Conditional("NoBug")]
[Conditional("Li")]
public static void Func()
{Console.WriteLine("Created by Li, NoBug"); }
//单括号叠加
[Conditional("NoBug"),Conditional("Li")]
public static void Func()
{Console.WriteLine("Created by Li, NoBug"); }

预定义的特性

obsolete类用来指定该类的成员已经过时,在程序使用这个成员的时候会给出提示。

obsolete类有三种重载

public ObsoleteAttribute()

public ObsoleteAttribute(string message)  参数说明: message:描述了可选的变通方法文本字符串。

public ObsoleteAttribute(string message, bool error)  参数说明:message:描述了可选的变通方法文本字符串。  error:true 如果使用过时的元素将生成编译器错误;false 如果使用它将生成编译器警告。

比如如果我们的Person类的SayHello()方法现在不能用了,不允许程序员使用这个方法,那么我们就可以在Person类的SayHello方法前面加 [Obsolete("该方法已经过期,请找最新的方法", true)]

这样当我们在主程序中用到Person类的SayHello方法的时候程序就会报错。当然,如果第二个参数设置为false时,在调用该成员的时候不会报错,但是会报出警告。

class Person
            //人的姓名储存字段和属性 
            private string name;
            public string Name
                set { name = value; }
                get { return name; }
            //人的年龄储存字段和属性 
            private int age;
            public int Age
                set { age = value; }
                get { return age; }
            //人的性别储存字段和属性 
            private char sex;
            public char Sex
                set { sex = value; }
                get { return sex; }
           [Obsolete("该方法已经过期,请找最新的方法", true)]/*这样编译就会报错*/
           如果是[[Obsolete("该方法已经过期,请找最新的方法", false)]]/*编译警告*/
            //人的打招呼方法 
            public void SayHello()
                Console.WriteLine($"大家好,我叫{this.Name},我今年{this.Age}岁了,我的性别是{this.Sex}");

Attribute作为编译器的指令

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

Conditional :起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译。一般在程序调试的时候使用。

DllImport :用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。

Obsolete :这个属性用来标记当前的方法已经被废弃,不再使用了。

#define debug   //定义条件
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ConsoleApp1
    class Program
//这个执行时注释掉,因为你没有外部的dll
        [DllImport("User32.dll")]
        public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
        [Conditional("debug")]//这个时候方法要属性就必须定义debug
        private static void DisplayRunningMessage()
            Console.WriteLine($"开始运行Main子程序。当前时间是{DateTime.Now}");
        [Conditional("debug")]
        [Obsolete]//会报告警告,但编译不出错
        private static void DisplayDebugMessage()
            Console.WriteLine("开始Main子程序");
        static void Main(string[] args)
            DisplayRunningMessage();
            DisplayDebugMessage();
            MessageBox(0, "Hello", "Message", 0); 
            Console.ReadKey();
}

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:

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

public MyCustomAttribute : Attribute { ... }

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

自定义或控制特性的使用

对于一个自定义的Attribute,可以通过AttributeUsage的Attribute来限定你的Attribute所施加的元素的类型。代码形式如下:

[AttriubteUsage(参数设置)] public 自定义Attribute : Attribute { ... }

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

//
    // 摘要:
    //     指定可应用属性的应用程序元素。
    [ComVisible(true)]
    [Flags]
    public enum AttributeTargets
        // 摘要:
        //     特性可以应用于程序集。
        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,
        // 摘要:
        //     特性可以应用于泛型参数。
        GenericParameter = 16384,
        // 摘要:
        //     特性可以应用于任何应用程序元素。
        All = 32767
    }

作为参数的AttributeTarges的值允许通过“或”操作来进行多个值得组合,如果你没有指定参数,那么默认参数就是All 。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)]


AttributeUsage除了继承Attribute 的方法和属性之外,还定义了以下三个属性:

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

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

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

示例:在Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用

[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
public class HelpAttribute : Attribute
    public HelpAttribute(string Description_in)
        this.description = Description_in;
    protected string description;
    public string Description
            return this.description;
//如果是这样的话
[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
            return this.description;
那么这个特性只能运用在类上
[Help(class)]
class Myclass
而下列报错
[Help(class)]
class Myclass
[Help(method)]//不能运用在方法
public void Mymethod()
下面考虑一下AllowMultiple = false。它规定了特性不能被重复放置多次
[Help(class)]
[Help(class1)]//错误,重复了
class Myclass
下面是关于Inherited 的讲解
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method |
                AttributeTargets.Property | AttributeTargets.Field,
                Inherited = true)]
public class InheritedAttribute : Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method |
                AttributeTargets.Property | AttributeTargets.Field,
                Inherited = false)]
public class NotInheritedAttribute : Attribute
using System;
using System.Reflection;
[InheritedAttribute]
public class BaseA
    [InheritedAttribute]
    public virtual void MethodA()
public class DerivedA : BaseA
    public override void MethodA()
[NotInheritedAttribute]
public class BaseB
    [NotInheritedAttribute]
    public virtual void MethodB()
public class DerivedB : BaseB
    public override void MethodB()
public class Example
    public static void Main()
        Type typeA = typeof(DerivedA);
        Console.WriteLine($"DerivedA has Inherited attribute: {typeA.GetCustomAttributes(typeof(InheritedAttribute), true).Length > 0}");
        MethodInfo memberA = typeA.GetMethod(nameof(DerivedA.MethodA));
        Console.WriteLine($"DerivedA.MemberA has Inherited attribute: {memberA.GetCustomAttributes(typeof(InheritedAttribute), true).Length > 0}\n");
        Type typeB = typeof(DerivedB);
        Console.WriteLine($"DerivedB has NotInherited attribute: {typeB.GetCustomAttributes(typeof(NotInheritedAttribute), true).Length > 0}");
        MethodInfo memberB = typeB.GetMethod(nameof(DerivedB.MethodB));
        Console.WriteLine($"DerivedB.MemberB has NotInherited attribute: {memberB.GetCustomAttributes(typeof(NotInheritedAttribute), true).Length > 0}");
// The example displays the following output:
//       DerivedA has Inherited attribute: True
//       DerivedA.MemberA has Inherited attribute: True
//       DerivedB has NotInherited attribute: False
//       DerivedB.MemberB has NotInherited attribute: False

举个栗子

using System;
using System.Reflection;
namespace BugFixApplication
   // 一个自定义特性 BugFix 被赋给类及其成员
   [AttributeUsage(AttributeTargets.Class |
   AttributeTargets.Constructor |
   AttributeTargets.Field |
   AttributeTargets.Method |
   AttributeTargets.Property,
   AllowMultiple = true)]
   public class DeBugInfo : System.Attribute
      private int bugNo;
      private string developer;
      private string lastReview;
      public string message;
      public DeBugInfo(int bg, string dev, string d)
         this.bugNo = bg;
         this.developer = dev;
         this.lastReview = d;
      public int BugNo
            return bugNo;
      public string Developer
            return developer;
      public string LastReview
            return lastReview;
      public string Message
            return message;
            message = value;
   [DeBugInfo(45, "Zara Ali", "12/8/2012",
        Message = "Return type mismatch")]
   [DeBugInfo(49, "Nuha Ali", "10/10/2012",
        Message = "Unused variable")]
   class Rectangle
      // 成员变量
      protected double length;
      protected double width;
      public Rectangle(double l, double w)
         length = l;
         width = w;
      [DeBugInfo(55, "Zara Ali", "19/10/2012",
           Message = "Return type mismatch")]
      public double GetArea()
         return length * width;
      [DeBugInfo(56, "Zara Ali", "19/10/2012")]
      public void Display()
         Console.WriteLine("Length: {0}", length);
         Console.WriteLine("Width: {0}", width);
         Console.WriteLine("Area: {0}", GetArea());
   }//end class Rectangle  
   class ExecuteRectangle
      static void Main(string[] args)
         Rectangle r = new Rectangle(4.5, 7.5);
         r.Display();
         Type type = typeof(Rectangle);
         // 遍历 Rectangle 类的特性
         foreach (Object attributes in type.GetCustomAttributes(false))
            DeBugInfo dbi = (DeBugInfo)attributes;
            if (null != dbi)
               Console.WriteLine("Bug no: {0}", dbi.BugNo);
               Console.WriteLine("Developer: {0}", dbi.Developer);
               Console.WriteLine("Last Reviewed: {0}",
                                        dbi.LastReview);
               Console.WriteLine("Remarks: {0}", dbi.Message);
         // 遍历方法特性
         foreach (MethodInfo m in type.GetMethods())
            foreach (Attribute a in m.GetCustomAttributes(true))
               DeBugInfo dbi = (DeBugInfo)a;
               if (null != dbi)
                  Console.WriteLine("Bug no: {0}, for Method: {1}",
                                                dbi.BugNo, m.Name);
                  Console.WriteLine("Developer: {0}", dbi.Developer);
                  Console.WriteLine("Last Reviewed: {0}",
                                                dbi.LastReview);
                  Console.WriteLine("Remarks: {0}", dbi.Message);
         Console.ReadLine();
Length: 4.5
Width: 7.5
Area: 33.75
Bug No: 49
Developer: Nuha Ali
Last Reviewed: 10/10/2012
Remarks: Unused variable
Bug No: 45
Developer: Zara Ali
Last Reviewed: 12/8/2012
Remarks: Return type mismatch
Bug No: 55, for Method: GetArea
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: Return type mismatch
Bug No: 56, for Method: Display
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: 



如範例的輸出所示, DerivedA DerivedA.MethodA 會繼承 InheritedAttribute 屬性,但 DerivedB DerivedB.MethodB 不會繼承 NotInheritedAttribute 屬性。

Flags特性

首先是枚举的定义上 ,要加上 [Flags] 特性标签,并且定义 一般都是 2的n次方,主要是便于位移运算

[Flags]
public enum DataSource
    /// <summary>
    ///     本地缓存
    /// </summary>
    [Description("本地缓存")]
    LocalCache = 1,
    /// <summary>
    ///    分布式缓存
    /// </summary>
    [Description("分布式缓存")]
    DistributeCache = 2,
    /// <summary>
    ///     数据库
    /// </summary>
    [Description("数据库")]
    DB = 4,
public UserEntity  GetUserInfo(DataSource dataSources)
    var xxxx = new UserEntity();
    if(dataSources.HasFlags(DataSource.Local)
        //从本地缓存中获取
        return xxxx;
    if(dataSources.HasFlags(DataSource.Distribution)
        //从分布式缓存中获取
        //更新本地缓存
        return xxxx;
    if(dataSources.HasFlags(DataSource.DB)
        //从DB中获取
        //更新分布式缓存
        //更新本地缓存