C#中的Attribute屬性(特性)用法

經常在C#的範例程式碼中,看到在類別Class、方法Method上面,帶了一個中括號,裡面寫了一些蠻語意化的字串。這個東西在C#語言裡面用到不少,它源自於System.Attribute,並且在.NET的整體架構中佔了很重要的一環。

Attribute在英文中的意思是指「屬性」,但是這與我們一般在程式語言中理解的屬性是完全不一樣的意思的,更進階一點來說應該翻成「特性」才對。一般來說,在普通的程式碼環境根本不會用到這種Attribute語法,只有在特別的時期,例如Win32 DLL的引用[DllImport],或者是Object Serializable時,才會使用到,Attribute是編譯時期的產物,像剛才講的這兩個時機點,都是要跟編譯器描述(講)一些資訊,以利其編譯之動作。

更簡單的說,Attritube最主要的目的,就是可以提供延伸的資訊給程式碼參考,並可動態去修正後續要進行的動作。

基於上述的理由,我們建立一個CMD環境的程式碼,其中包含了一個自訂的特性類別「Writer」,用來記錄這一段程式是由誰來撰寫,以及相關的參考參數可以取出。另外一個「SomeClass」則是真正的把Attribute的寫法使用上去,你可以發現它甚至可以拿來當成程式碼的註解來使用(這也是這個無用的範例假設的情境),無論是Class或Method都可以支援Attribute,且可以不只一個Attribute。

※註:可支援Attribute的寫法包含了組件Assembly、Module、Type、Property、Event、Field、Method、Pparam、Return等。

最後我們可以從主控台Main這邊,來進行這兩個類別的操作,程式碼以及相關說明如下:

namespace Attribute
{
	class Program
	{
		static void Main(string[] args)
		{
			//取出操作對象的Type
			System.Type oType = typeof(SomeClass);
			//利用反射取出成員資訊(記住Reflection蠻很耗效能)
			System.Reflection.MemberInfo oItemInfo = oType;
			
			/*取出類別上方的Attribute*/
			//取出類別Class中隱含的屬性(0筆至多筆)
			foreach (Writer oWriter in System.Attribute.GetCustomAttributes(oItemInfo))
			{
				System.Console.Write("Writer id={0}, ", oWriter.id.ToString());
				System.Console.WriteLine("name={0}", oWriter.name);
			}
			
			/*取出方法上方的Attribute*/
			//取出這個類別裡面的所有方法
			foreach (System.Reflection.MethodInfo oMethod in oType.GetMethods())
			{
				//取出類別Method中隱含的屬性(0筆至多筆)
				foreach (var oTemp in System.Attribute.GetCustomAttributes(oMethod))
				{
					//因為有可能取到.ToString()之類的繼承方法,那些並不是我們所需要的,所以必須過濾掉
					if (oTemp.GetType() == typeof(Writer))
					{
						Writer oWriter = (Writer)oTemp;
						System.Console.Write("Method Name: {0}; ", oMethod.Name);
						System.Console.Write("Writer id={0}, ", oWriter.id.ToString());
						System.Console.Write("name={0}, ", oWriter.name);
						System.Console.WriteLine("note={0}", oWriter.note);
					}
				}
			}

			System.Console.Read();
		}
	}

	//無聊的示範類別(單純的示範這個程式用來記載某個類別方法是「誰」寫的)
	[Writer(id=777, name="John")]
	[Writer(id=888, name="Mary")]
	class SomeClass
	{
		[Writer(id=901, name="Programer-1", note="程式設計師一")]
		[Writer(id=902, name="Programer-2", note="程式設計師二")]
		public string ADD()
		{ return "Method ADD has been run."; }

		[Writer(id=903, name="Programer-3", note="程式設計師三")]
		public string DEL()
		{ return "Method DEL has been run."; }
	}

	//自訂Attribute類別,一定要繼承System.Attribute
	//且可藉由上方的Attribute來進行細部的設定(例如多Attribute的AllowMultiple設定)
	[System.AttributeUsage(
		System.AttributeTargets.All, 
		Inherited=false, 
		AllowMultiple=true)]
	class Writer : System.Attribute
	{
		public int id { get; set; }
		public string name { get; set; }
		public string note { get; set; }
	}
}

由上面的程式碼中,我們可以在執行期時,求得正在運行中的類別的「相關註解」,也可以針對正在運行中的類別中的某個方法進行求取「相關註解」,至於這個「相關註解」到底要幹嘛,就得看你後續的程式的需求了。執行出來的畫面如下:

在Attribute的應用中,如果你用到的是自訂Attribute特性,其實最後大多是準備要用於晚期繫結,不然在執行期要這些無聊的類別註解資訊要幹嘛?晚期繫結在.NET裡面的實作就叫做Reflection,中文稱為反射,這個留在「C#中的Reflection反射(反映)用法」中再來探討。

C# Attribute 屬性 特性 中括號 Reflection