先明確一個概念:
元數據。.NET中元數據是指程序集中的命名空間、類、方法、屬性等信息。這些信息是可以通過Reflection讀取出來的。
再來看個例子:
#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(); } } }
運行結果:
將#define BUG注釋掉,#define NOBUG的注釋取消,重新運行的結果如下:
那么可以理解為,上述代碼中的[Conditional()]起到了條件的作用。這就是一種特性。
特性是用於在運行時傳遞程序中各種元素(類、方法、結構、枚舉等)的行為信息的聲明性標簽。可以通過使用特性向程序中添加聲明性信息。這些信息添加到了元數據中。.NET框架中的特性包括Common Attributes和Custom Attributes。其中Common Attributes包括Global Attributes, Obsolete Attributes, Conditional Attributes, Caller Info Attributes。
Common Attributes
- Global Attributes 全局特性,應用於整個程序集。置於using后代碼最上端。全局特性提供一個程序集的信息,分為三種類別:
- Assembly identity attributes 程序集標識符特性。name, version, and culture這三個特性用於確定一個程序集的標識符。可以查看一下項目中Properties下面的AssemblyInfo.cs文件。
- Informational attributes 信息特性,提供一些與公司或產品相關的信息,包括AssemblyProductAttribute,AssemblyTrademarkAttribute等。
- Assembly manifest attributes 程序集清單特性。包括title, description, default alias, and configuration。
- Conditional Attributes 標記了一個條件方法,其執行依賴於指定的預處理標識符(#define),實例就是上面那個。條件特性可以同時使用多個。比如[Conditional("A"), Conditional("B")]。
- Obsolete Attributes 標記了不應被使用的程序實體。它可以讓您通知編譯器丟棄某個特定的目標元素。其語法表示為:[Obsolete(message)]、[Obsolete(message, iserror)]
其中,message是一個string,描述項目為什么過時以及該替代使用什么;iserror是一個bool,如果為true,編譯器應該把項目的使用當做一個錯誤,默認值是false,此時編譯器生成一個警告。比如下面的例子:
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(); } } }
這時程序無法編譯,顯示如下的錯誤:
- Caller Info Attributes 通過使用該特性,可以獲取調用某個方法的調用者的信息。比如代碼的文件路徑、代碼的行等。比如下面所示的代碼。
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/
4.https://blog.csdn.net/xiaouncle/article/details/70216951