C# 的特性 Attribute(学习心得 28)

    技术2022-07-11  111

    特性(Attribute):用于,在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的 行为信息 的 声明性标签。

    您可以通过使用 特性 向程序添加 声明性信息 。

    一个 声明性标签 是通过放置在它所应用的元素前面的方括号([ ])来描述的。

    特性(Attribute)用于添加 元数据,如编译器指令和注释、描述、方法、类等其他信息。

    .Net 框架提供了两种类型的特性:预定义特性和自定义特性。

    文章目录

    一、规定特性(Attribute)二、预定义特性(Attribute)2.1 AttributeUsage2.2 Conditional2.3 Obsolete 三、创建自定义特性(Attribute)3.1 声明自定义特性3.2 构建自定义特性3.3 应用自定义特性

    一、规定特性(Attribute)

    语法:

    [attribute(positional_parameters, name_parameter = value, ...)] element

    特性(Attribute)的名称和值是在方括号内规定的。

    放置在它所应用的元素之前。

    positional_parameters 规定必需的信息。

    name_parameter 规定可选的信息。

    二、预定义特性(Attribute)

    .Net 框架提供了三种预定义特性:

    AttributeUsageConditionalObsolete

    2.1 AttributeUsage

    预定义特性 AttributeUsage 描述了如何使用一个 自定义特性 类。

    它规定了特性可应用到的项目的类型。

    语法:

    [AttributeUsage( validon, AllowMultiple=allowmultiple, Inherited=inherited )]

    参数:

    参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

    例:

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

    2.2 Conditional

    这个预定义特性标记了一个 条件方法,其执行依赖于指定的 预处理标识符。

    它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。

    例如,当调试代码时显示变量的值。

    语法:

    [Conditional( conditionalSymbol )]

    例:

    [Conditional("DEBUG")]

    例:演示该特性

    #define D1 // 这里就是选择开关,选择了运行 D1 using System; using System.Diagnostics; namespace helloworld { public class Myclass { [Conditional("D1")] // 仅在 D1 情况下运行 public static void M1(string msg) { Console.WriteLine(msg); } [Conditional("D2")] // 仅在 D2 情况下运行 public static void M2(string msg) { Console.WriteLine(msg); } static void Main() { Myclass.M1("In Function 1."); Myclass.M2("In Function 2."); Console.ReadKey(); } } }

    运行结果:

    In Function 1.

    这里显示,只有 D1 条件下的方法被运行了。

    如果我们改成 #define D2,则 D2 条件下的被运行。

    总结:

    使用Conditional是封闭#if和#endif内部方法的替代方法,它更整洁,减少了出错的机会。

    这里有个注意点,那就是如果设定 [Conditional("DEBUG")] 这里用的是 “DEBUG”,那么在 Visual Studio 中会出现奇怪的情况,那就是即使你定义 #define D1,那 “DEBUG” 下的方法依然会运行。

    从官网文档查询到,那是因为:

    在 Visual Studio C#和 Visual Basic 项目中,默认情况下,为调试版本定义 “debug” 条件编译符号,同时为调试版本和发布版本定义 “TRACE” 符号。

    也就是说,我们在 Visual Basic 运行程序本身就已经定义了 #define DEBUG 这个前提,不管你怎么设定 #define,这个前提一直在。同时,官方文档也给出了禁用的方法,需要用的时候可以去查阅。

    2.3 Obsolete

    这个预定义特性标记了不应被使用的程序实体。

    它可以让您通知编译器丢弃某个特定的目标元素。

    例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

    语法:

    [Obsolete( message )] [Obsolete( message, iserror )]

    参数:

    message,是一个字符串,描述项目为什么过时以及该替代使用什么。iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。

    例:

    using System; public class MyClass { [Obsolete("Don't use OldMethod, use NewMethod instead", true)] static void OldMethod() { Console.WriteLine("It is the old method"); } static void NewMethod() { Console.WriteLine("It is the new method"); } public static void Main() { OldMethod(); } }

    运行结果:

    Don't use OldMethod, use NewMethod instead

    三、创建自定义特性(Attribute)

    .Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。

    该信息根据设计标准和应用程序需要,可与任何目标元素相关。

    创建并使用自定义特性包含四个步骤:

    声明自定义特性构建自定义特性在目标程序元素上应用自定义特性通过反射访问特性

    最后一个步骤,包含编写一个简单的程序,来读取元数据以便查找各种符号。

    元数据是用于描述其他数据的数据和信息。

    该程序应使用反射来在运行时访问特性。

    3.1 声明自定义特性

    一个新的自定义特性应派生自 System.Attribute 类。

    例:声明了一个名为 DeBugInfo 的自定义特性

    // 一个自定义特性 BugFix 被赋给类及其成员 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class DeBugInfo : System.Attribute

    3.2 构建自定义特性

    例:构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。

    存储下面的信息:

    bug 的代码编号辨认该 bug 的开发人员名字最后一次审查该代码的日期一个存储了开发人员标记的字符串消息

    该 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。

    所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。

    每个特性必须至少有一个构造函数。

    必需的定位( positional)参数应通过构造函数传递。

    程序:

    // 一个自定义特性 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 { get { return bugNo; } } public string Developer { get { return developer; } } public string LastReview { get { return lastReview; } } public string Message { get { return message; } set { message = value; } } }

    3.3 应用自定义特性

    通过把特性放置在紧接着它的目标之前,来应用该特性

    例:

    [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()); } }

    我们可以使用反射 Reflection 类对象来检索这些信息。

    例:

    using System; // 1. 创建一个自定义特性 // 描述如何使用一个自定义特性 SomethingAttribute [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)] //********自定义特性SomethingAttribute**************// public class SomethingAttribute : Attribute { private string name; // 名字 private string data; // 日期 public string Name // 属性 { get { return name; } set { name = value; } } public string Data // 属性 { get { return data; } set { data = value; } } // 构造函数传递必须的定位参数 public SomethingAttribute(string name) { this.name = name; this.name = name; } } // 2. 实例化自定义特性 [Something("Amy", Data = "2018-06-18")] [Something("Jack", Data = "2018-06-18")] class Test{} // 3. 获取自定义特性的中的变量(该部分待验证) Type t = typeof(Test); var something = t.GetCustomAttributes(typeof(SomethingAttribute),true); foreach(SomethingAttribute each in something) { Console.WriteLine("Name:{0}", each.Name); Console.WriteLine("Data:{0}", each.Data); }
    Processed: 0.011, SQL: 9