先明确一個概念:
中繼資料。.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