先明確一個概念: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/