先明确一个概念:html
元数据。.NET中元数据是指程序集中的命名空间、类、方法、属性等信息。这些信息是能够经过Reflection读取出来的。app
再来看个例子:框架
#define BUG
//#define NOBUG
using System; using System.Diagnostics; namespace AttributeExample { public static class AttributeTest { [Conditional("BUG")] public static void OutputDebug(string msg) { Console.WriteLine(msg + " : bug"); } [Conditional("NOBUG")] public static void OutputMessage(string msg) { Console.WriteLine(msg + " : no bug"); } } class Program { static void Main(string[] args) { AttributeTest.OutputDebug("Function"); AttributeTest.OutputMessage("Function"); Console.Read(); } } }
运行结果:ide
将#define BUG注释掉,#define NOBUG的注释取消,从新运行的结果以下:函数
那么能够理解为,上述代码中的[Conditional()]起到了条件的做用。这就是一种特性。ui
特性是用于在运行时传递程序中各类元素(类、方法、结构、枚举等)的行为信息的声明性标签。能够经过使用特性向程序中添加声明性信息。这些信息添加到了元数据中。.NET框架中的特性包括Common Attributes和Custom Attributes。其中Common Attributes包括Global Attributes, Obsolete Attributes, Conditional Attributes, Caller Info Attributes。this
Common Attributesspa
其中,message是一个string,描述项目为何过期以及该替代使用什么;iserror是一个bool,若是为true,编译器应该把项目的使用当作一个错误,默认值是false,此时编译器生成一个警告。好比下面的例子:.net
using System; using System.Diagnostics; namespace AttributeExample { class Program { [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"); } static void Main(string[] args) { OldMethod(); Console.Read(); } } }
这时程序没法编译,显示以下的错误:设计
using System; using System.Runtime.CompilerServices; namespace AttributeExample { class Program { public static void DoSomething() { TraceMessage("Something happened."); } public static void TraceMessage(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { Console.WriteLine("message: " + message); Console.WriteLine("member name: " + memberName); Console.WriteLine("source file path: " + sourceFilePath); Console.WriteLine("source line number: " + sourceLineNumber); } static void Main(string[] args) { DoSomething(); Console.Read(); } } }
运行结果如图所示:
Custom Attributes
.NET框架容许建立自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序须要,可与任务目标元素相关。建立并使用自定义特性包含四个步骤:
1. 声明自定义特性
一个新的自定义特性应派生自System.Attribute类,好比:
// 声明名为FirstAttribute的自定义特性
[AttributeUsage(AttributeTargets.Class, Inherited =false)] class FirstAttribute : Attribute { }
// 声明名为SecondAttribute的自定义特性 [AttributeUsage(AttributeTargets.Class)] class SecondAttribute : Attribute { }
// 声明名为ThirdAttribute的自定义特性 [AttributeUsage(AttributeTargets.Class, AllowMultiple =true)] class ThirdAttribute : Attribute { }
又好比:
// 声明名为CustomAttribute的自定义特性
[AttributeUsage(AttributeTargets.Class| AttributeTargets.Constructor| AttributeTargets.Field| AttributeTargets.Method| AttributeTargets.Property, AllowMultiple =true)] public class CustomAttributes : System.Attribute{}
2.构建自定义特性
上面已经声明了一个名为CustomAttribute的自定义特性,如今构建这个特性。该特性将存储调试程序得到的信息,包括:bug的代码编号、辨认该bug的开发人员名字、最后一次审查该代码的日期以及一个存储了开发人员标记的字符串消息。
CustomAttribute类将带有三个用于存储前三个信息的私有属性和一个用于存储消息的公有属性。所以bug编号、开发人员名字和审查日期将是CustomAttribute类的必需的定位(positional)参数,消息将是一个可选的命名(named)参数。每一个特性必须至少有一个构造函数。必需的定位参数应经过构造函数传递,以下面的代码所示:
[AttributeUsage(AttributeTargets.Class| AttributeTargets.Constructor| AttributeTargets.Field| AttributeTargets.Method| AttributeTargets.Property, AllowMultiple =true)] public class CustomAttributes : System.Attribute { public int BugNo { get; } public string Developer { get; } public string LastReview { get; } public string Message { get; set; } public CustomAttributes(int BugNo, string Developer, string LastReview) { this.BugNo = BugNo; this.Developer = Developer; this.LastReview = LastReview; } }
3.在目标程序元素上应用自定义特性
经过把特性放置在紧接着它的目标以前,来应用该特性:
[CustomAttributes(45,"Zara Ali","12/8/2012", Message ="Return type mismatch")] [CustomAttributes(49,"Nuha Ali","10/10/2012",Message ="Unused variable")] class Rectangle { protected double length; protected double width; public Rectangle(double length, double width) { this.length = length; this.width = width; } [CustomAttributes(55,"Zara Ali","19/10/2012", Message ="Return type mismatch")] public double GetArea() { return length * width; } [CustomAttributes(56,"Zara Ali", "19/10/2012")] public void Display() { Console.WriteLine("Length: {0}", length); Console.WriteLine("Width: {0}", width); Console.WriteLine("Area: {0}", GetArea()); } }
4.经过反射访问特性
别忘了使用using System.Reflection;
class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(4.5, 7.5); r.Display(); object[] attrs = r.GetType().GetCustomAttributes(false); // 遍历Rectangle类的特性 foreach(Attribute attr in attrs) { CustomAttributes attribute = (CustomAttributes)attr; if(null != attribute) { Console.WriteLine("Bug no: {0}", attribute.BugNo); Console.WriteLine("Developor: {0}", attribute.Developer); Console.WriteLine("Last Reviewed: {0}", attribute.LastReview); Console.WriteLine("Remarks: {0}", attribute.Message); } } // 遍历方法特性 object[] methods = r.GetType().GetMethods(); foreach(MethodInfo method in methods) { foreach(object attribute in method.GetCustomAttributes(true)) { CustomAttributes attr = attribute as CustomAttributes; if(null != attr) { Console.WriteLine("Bug no: {0}, for Method: {1}", attr.BugNo, method.Name); Console.WriteLine("Developer: {0}", attr.Developer); Console.WriteLine("Last Reviewed: {0}", attr.LastReview); Console.WriteLine("Remarks: {0}", attr.Message); } } } Console.Read(); } }
运行结果如图所示:
参考文献:
1.https://www.runoob.com/csharp/csharp-attribute.html
2.https://www.runoob.com/csharp/csharp-reflection.html
3.https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/