C#反射和特性2 特性

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

将应用了特性的程序结构叫做目标(target)。

设计用来获取和使用元数据的程序叫做特性的消费者(consumer)。

.Net预定了很多特性,我们也可以声明自定义特性。

开发人员将特性应用于程序,编译器获取源代码并从特性中产生元数据,然后把元数据放到程序集中,消费者程序可以获取特性的元数据以及程序中其他组件的元数据。

特性通常以Attribute作为后缀,当为目标应用特性时,可以不使用后缀。

几个官方特性:

1、Obsolete,用于告知程序已经过期(提示警告或错误)

2、Conditional,允许包括或排斥特定方法的所有调用。为方法声明应用Conditional特性并把编译符作为参数来使用

    class Program
    {
        [Obsolete("This method is obsolute")]
        static void PrintOut(string str)
        {
            Console.WriteLine(str);
        }
        [Conditional("DoTrace")]
        static void TraceMessage(string str)
        {
            Console.WriteLine(str);
        }
        static void Main(string[] args)
        {
            TraceMessage("Start of Main");
            PrintOut("hello world");
            TraceMessage("End of Main");
        }
    }

被标注了Obsolete的程序结构会被标注为过期,但代码仍然可以正常运行,只是编译时显示对应的过期警告提示,如果想不删除这个程序结构但也不希望任何人使用,则提供:

[Obsolete("This method is obsolute",true)]

它会在编译时显示该程序结构的调用是错误的。

被标注了Conditional("xx")的程序结构会在编译时被编译器跳过,除非在using上面定义宏

#define Dotrace

3、调用者信息特性

        static void MyTrace(string message,[CallerFilePath]string fileName="",[CallerLineNumber]int lineNumber=0,[CallerMemberName]string callingMember="")
        {
            Console.WriteLine("File:{0}", fileName);
            Console.WriteLine("Line:{0}", lineNumber);
            Console.WriteLine("Called From:{0}", callingMember);
            Console.WriteLine("Message:{0}", message);
        }
...
MyTrace("Simple message");

File:E:\Study\C#Concepts\Attribute\Attribute\Program.cs
Line:33
Called From:Main
Message:Simple Message

CallerFilePath,CallerLineNumber,CallerMemberName用于访问文件路径、代码行数、调用成员的名称等源代码信息,但这些特性智能用于方法中的可选参数。

4、调试时跳过某些代码

[DebugStepThrough]

5、序列化

 [Serializable]特性本身并不序列化,它只是指示这个对象可以被序列化。

6、自定义特性

要声明一个自定义特性,需要作如下工作:

A、声明一个派生自System.Attribute的类,通常还带有sealed保证安全性

B、起一个带有Attribute作为后缀的名字

C、能持有的公共成员只能是字段、属性、构造函数

    
    [AttributeUsage(AttributeTargets.Class)]
public sealed class MyAttributeAttribute:System.Attribute { public string Description { get; set; } public string Version { get; set; } public MyAttributeAttribute(string desc,string version) { Description = desc; Version = version; } }
    [Serializable]
    [MyAttribute("Class Attribute","Version 1.0.0")]
    class Attribute
    {
        public int Id { get; set; }
    }

在使用自定义特性时,应注意以下实践:

1、特性类应该标识目标结构的一些状态

2、如果特性需要某些字段,可以通过包含具有位置参数的构造函数来收集数据,可选字段可以采用命名参数来按需初始化。

3、除了属性外,不实现公共方法或其他函数成员

4、特性类声明为密封类

5、在特性声明中使用AttributeUsage来显式指定特性目标组

 

可以通过Type对象的IsDefined来检查某个特性是否应用到了某个类上。

            Type t = attribute.GetType();
            bool isDefined = t.IsDefined(typeof(MyAttributeAttribute), false);
            if (isDefined)
                Console.WriteLine($"MyAttribute is applied to type {t.Name}");       

输出:MyAttribute is applied to type Attribute

也可以:

            object[] attrArr = t.GetCustomAttributes(false);
            foreach(MyAttributeAttribute item in  attrArr)
            {
                MyAttributeAttribute attr = item as MyAttributeAttribute;
                if(null!=attr)
                {
                    Console.WriteLine($"Decription:{attr.Description}");
                    Console.WriteLine($"Version:{attr.Version}");
                }

            }

输出:

Decription:Class Attribute
Version:Version 1.0.0

posted @ 2020-05-27 09:46  NicolasLiaoRan  阅读(178)  评论(0编辑  收藏  举报