18.1 使用定製特性 FCL 中的幾個常用定製特性. DllImport 特性應用於方法,告訴 CLR 該方法的實現位於指定 DLL 的非托管代碼中. Serializable 特性應用於類型,告訴序列化格式化器一個實例的欄位可以序列化和反序列化. AssemblyVersion 特性應用於程式集 ...
18.1 使用定製特性
- FCL 中的幾個常用定製特性.
- DllImport 特性應用於方法,告訴 CLR 該方法的實現位於指定 DLL 的非托管代碼中.
- Serializable 特性應用於類型,告訴序列化格式化器一個實例的欄位可以序列化和反序列化.
- AssemblyVersion 特性應用於程式集,設置程式集的版本號.
- Flags特性應用於枚舉類型,枚舉類型就成了位標誌集合.
- C# 允許用一個首碼明確指定特性要應用於的目標元素.有時可省略,編譯器能推斷;有時則必須指定首碼.
using System;
[assembly: SomeAttr] //應用於程式集
[module: SomeAttr] //應用於模塊
[type: SomeAttr] //應用於類型
internal sealed class SomeType<[typevar: SomeAttr] T> //應用於泛型類型變數
{
[field: SomeAttr] //應用於欄位
public int SomeField = 0;
[return: SomeAttr] //應用於返回值
[method: SomeAttr] //應用於方法
public int SomeMethod(
[param: SomeAttr] //應用於參數
int someParam) { return someParam; }
[property: SomeAttr] //應用於屬性
public string SomeProp {
[method: SomeAttr] //應用於get訪問器方法
get { return null; }
}
[event: SomeAttr] //應用於事件
[field: SomeAttr] //應用於編譯器生成的欄位
[method: SomeAttr] //應用於編譯器生成的add & remove方法
public event EventHandler SomeEvent;
}
- CLS 要求定製特性類必須直接或間接從公共抽象類 System.Attribute 派生.
- 應用特性時,c# 編譯器允許省略 Attribute 尾碼以減少打字量,並提升源代碼的可讀性.
特性是類的實例,語法類似於調用類的某個實例構造器.如:
[DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true)]
- "Kernel32" 是構造器的參數,稱為定位參數,必須指定.
- CharSet 和 SetLastError 用於設置欄位或屬性,稱為命名參數,是可選的.
- 在C#中,既可將每個特性都封閉到一對方括弧中,也可在一對方括弧中封閉多個以逗號分隔的特性.如果特性類的構造器不獲取參數,那麼圓括弧也可以省略,如:
- [Serializable][Flags]
- [Serializable,Flags]
- [FlagsAttribute,SerializableAttribute]
- [FlagsAttribute(), SerializableAttribute()]
18.2 定義自己的特性類
- 1) 從 Attribute 繼承; 2) 類名有 Attribute 尾碼(非必須).
AttributeUsage 特性是一個簡單的類,可利用它告訴編譯器定製特性的合法應用範圍.所有編譯器都內建了對該特性的支持.如:
[AttributeUsage(AttributeTargets.Enum, Inherited = false)] public class FlagsAttribute : Attribute { public FlagsAttribute() { } }
- AttributeUsageAttribute 類有一個公共構造器,它允許傳遞位標誌來指明特性的合法應用範圍.
- AttributeUsageAttribute 類有兩個公共屬性.其中 AllowMultiple 指示是否允許將該特性多次應用於同一個目標元素; Inherited 指出特性在應用於基類時,是否同時應用於派生類和重寫的方法.
如果忘記向自己的特性類應用 AttributeUsageAttribute 特性,則特性類預設為應用於所有目標元素,向每個目標元素都只能應用一次,而且可繼承.
18.3 特性構造器和欄位/屬性數據類型
- 定義特性類的實例構造器\欄位和屬性時,只允許Boolean,Char,Byte,SByte,Int16,UInt16,Int32,UInt32,Int64,UInt64,Single,Double,String,Type,Object 或枚舉類型.
- 應用特性時必須傳遞一個編譯時常量表達式,它與特性類定義的類型匹配.
- 定製特性: 它是類的實例,被序列化成駐留在元數據中的位元組流.遠行時可對元數據中的位元組進行反序列化,從而構造出類的實例.
18.4 檢測定製特性
- 有三個方法可以獲取與目標關聯的特性: IsDefined,GetCustomAttributes 和 GetCustomAttribute
- IsDefined 比另外兩個方法更高效,因為 IsDefined 不會構造特性對象,不會調用構造器,也不會設置欄位和屬性.
18.5 兩個特性實例的相互匹配
- System.Attribute 重寫了 Object 的 Equals 方法,利用反射來比較兩個特性對象中的欄位值(為每個欄位都調用 Equals).所有欄位都匹配就返回 true ; 否則返回 false.建議重寫 Equals 來移除反射的使用.
- System.Attribute 還公開了虛方法 Match ,其預設實現只是調用 Equals 方法並返回它的結果.