學習參考:

今天在討論IPC通訊契約的時候,盧工提到使用Attribute來描述具體的介面方法的命令資訊。發現對 Attribute的概念還不是很熟悉,因此對其進行學習梳理。

1、Attribute是什麼?它有什麼用?

先來看看官方的定義:

msdn文件對它的描述:公共語言執行時允許新增類似關鍵字的描述宣告,叫做attributes, 它對程式中的元素進行標註,如型別、欄位、方法和屬性等。Attributes和Microsoft .NET Framework檔案的元資料儲存在一起,可以用來向執行時描述你的程式碼,或者在程式執行的時候影響應用程式的行為。

簡單的定義 本質上是一個類,其為目標元素提供關聯附加資訊,並在執行期以反射的方式來獲取附加資訊。具體的特性實現方法,在接下來的討論中繼續深入。

看定義總是有距離感,還是看看實際的應用吧。

2、常用場景

.NET中常見的Attribute:

  • Conditional:起條件編譯的作用,只有滿足條件,才允許編譯器對它的程式碼進行編譯。一般在程式除錯的時候使用。
  • DllImport:用來標記非.NET的函式,表明該方法在一個外部的DLL中定義。
  • Obsolete:這個屬性用來標記當前的方法已經被廢棄,不再使用了。
  • Serializable:表明應用的元素可以被序列化
#define DEBUG //這裡定義條件

using System;
using System.Runtime.InteropServices;
using System.Diagnostics; namespace AttributeDemo
{
class MainProgramClass
{ [DllImport("User32.dll")]
public static extern int MessageBox(int hParent, string Message, string Caption, int Type); static void Main(string[] args)
{
DisplayRunningMessage();
DisplayDebugMessage(); MessageBox(, "Hello", "Message", ); Console.ReadLine();
} [Conditional("DEBUG")]
private static void DisplayRunningMessage()
{
Console.WriteLine("開始執行Main子程式。當前時間是" + DateTime.Now);
} [Conditional("DEBUG")]
[Obsolete]
private static void DisplayDebugMessage()
{
Console.WriteLine("開始Main子程式");
}
}
}

如果在一個程式元素前面宣告一個Attribute,那麼就表示這個Attribute被施加到該元素上,前面的程式碼,[DllImport]施加到MessageBox函式上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。Attribute類是在編譯的時候被例項化的,而不是像通常的類那樣在執行時候才例項化。

3、自定義特性

1、自定義的Attribute必須直接或者間接繼承System.Attribute。

2、所有自定義的特性名稱都應該有個Attribute字尾,命名規範為:"類名"+Attribute
3、使用AttributeUsage來限定你的Attribute 所施加的元素的型別,AttributeUsage本身也是一個Attribute。它有一個帶引數的構造器,這個引數是AttributeTargets的列舉型別

public enum AttributeTargets
{
All=,
Assembly=,
Module=,
Class=,
Struct=,
Enum=,
Constructor=,
Method=,
Property=,
Field=,
Event=,
Interface=,
Parameter=,
Delegate=,
ReturnValue=
}

此外,AttributeUsage還定義了以下三個屬性:

AllowMultiple::讀取或者設定這個屬性,表示是否可以對一個程式元素施加多個Attribute 。

Inherited:讀取或者設定這個屬性,表示是否施加的Attribute 可以被派生類繼承或者過載。

ValidOn::讀取或者設定這個屬性,指明Attribute 可以被施加的元素的型別。

下面是一個自定義特性的例子,摘自這裡

using System;
using System.Reflection; //應用反射技術獲得特性資訊 namespace Anytao.net
{
//定製特性也可以應用在其他定製特性上,
//應用AttributeUsage,來控制如何應用新定義的特性
[AttributeUsageAttribute(AttributeTargets.All, //可應用任何元素
AllowMultiple = true, //允許應用多次
Inherited = false)] //不繼承到派生類
//特性也是一個類,
//必須繼承自System.Attribute類,
//命名規範為:"類名"+Attribute。
public class MyselfAttribute : System.Attribute
{
//定義欄位
private string _name;
private int _age;
private string _memo; //必須定義其建構函式,如果不定義有編譯器提供無參預設建構函式
public MyselfAttribute()
{
}
public MyselfAttribute(string name, int age)
{
_name = name;
_age = age;
} //定義屬性
//顯然特性和屬性不是一回事兒
public string Name
{
get { return _name == null ? string.Empty : _name; }
} public int Age
{
get { return _age; }
} public string Memo
{
get { return _memo; }
set { _memo = value; }
} //定義方法
public void ShowName()
{
Console.WriteLine("Hello, {0}", _name == null ? "world." : _name);
}
} //應用自定義特性
//可以以Myself或者MyselfAttribute作為特性名
//可以給屬性Memo賦值
[Myself("Emma", , Memo = "Emma is my good girl.")]
public class Mytest
{
public void SayHello()
{
Console.WriteLine("Hello, my.net world.");
}
} public class Myrun
{
public static void Main(string[] args)
{
//如何以反射確定特性資訊
Type tp = typeof(Mytest);
MemberInfo info = tp;
MyselfAttribute myAttribute =
(MyselfAttribute)Attribute.GetCustomAttribute(info, typeof(MyselfAttribute));
if (myAttribute != null)
{
//嘿嘿,在執行時檢視註釋內容,是不是很爽
Console.WriteLine("Name: {0}", myAttribute.Name);
Console.WriteLine("Age: {0}", myAttribute.Age);
Console.WriteLine("Memo of {0} is {1}", myAttribute.Name, myAttribute.Memo);
myAttribute.ShowName();
} //多點反射
object obj = Activator.CreateInstance(typeof(Mytest)); MethodInfo mi = tp.GetMethod("SayHello");
mi.Invoke(obj, null);
Console.ReadLine();
}
}
}

4、使用FlagsAttribute修飾列舉

FlagsAttribute屬性就是列舉型別的一項可選屬性。它的主要作用是可以將列舉作為位域處理,所謂位域是單個儲存單元內相鄰二進位制位的集合。在.Net framework中有很多列舉都是用FlagsAttribute特性修飾,例如:正則表示式選項System.Text.RegularExpressions.RegexOptions、檔案監視中的檔案改變型別System.IO.WatcherChangeTypes、System.Web.UI.WebControls.DataControlRowState等等。

使用FlagsAttribute需要注意:

1、只有要對數值執行按位運算(AND、OR、XOR)時才對列舉使用 FlagsAttribute 自定義屬性。

2.、必須用 2 的冪(即 1、2、4、8 等)定義列舉常量。

using System;

class FlagsAttributeDemo
{
enum Color1 : short
{
Black = ,
Red = ,
Green = ,
Blue =
}; [FlagsAttribute]
enum Color2 : short
{
Black = ,
Red = ,
Green = ,
Blue =
}; static void Main()
{
Console.WriteLine("測試未使用FlagsAttribute屬性");
Color1 MyColor1 = Color1.Red | Color1.Blue & Color1.Green;
//我先不執行計算一下看看是那個:0001|0100&0010=0001 應該是Red
Console.WriteLine("MyColor1={0}", MyColor1);
Color1 MyColor_1 = Color1.Red | Color1.Blue;
//我先不執行計算一下看看是那個:0001|0100=0101 應該是5
Console.WriteLine("MyColor_1={0}",MyColor_1);
Console.WriteLine("測試使用FlagsAttribute屬性");
Color2 MyColor2 = Color2.Red | Color2.Blue;
//我先不執行計算一下看看是那個:0001|0100=0101應該是Red,Blue
Console.WriteLine("MyColor2={0}", MyColor2);
Console.ReadKey();
}
}