一篇文章說通C#的屬性Attribute
屬性Attributes這個東西,用好瞭可以省N多代碼。
一、屬性
屬性Attributes在C#中很常用,但事實上很多人對這個東西又很陌生。
從概念上講,屬性提供的是將元數據關系到元素的一種方式。
屬性使用的樣子,應該都見過:
[Flags] //Attribute public enum DayOfWeek { Sunday = 1, Monday = 2, Tuesday = 4, Wednesday = 8, Thursday = 16, Friday = 32, Saturday = 64 }
代碼中,Flags就是一個屬性。
通常,屬性會放在類、字段、方法等定義的上面,用來指定特定的內容。
.Net Framework框架提供瞭一些屬性。像常見的Serializable,用來告訴編譯器當前的類可以序列化成JSON或XML:
[Serializable] public class SerializableClass { /*...*/ }
需要註意的是,屬性在編譯時會嵌入到程序集中。這樣,我們可以使用反射來獲得相應的屬性值。
二、自定義屬性
自定義屬性用處很大,算是我自己比較常用的一個技術。
自定義屬性需要從System.Attribute抽象類來繼承。
想象一個場景。我們在構建一個手機類。我們需要一個屬性來表示手機一些信息,比方口牌和生產年份:
public class MobileInformationAttribute : Attribute { public string brand { get; set; } public int yearOfProduct { get; set; } public MobileInformationAttribute(string Brand, int YearOfProduct) { brand = Brand; yearOfProduct = YearOfProduct; } }
我們會註意到:屬性是一個類,和其它類一樣,擁有字段、方法、構造函數和其它成員。
三、使用屬性
前面說瞭,屬性可以放在類、字段、方法等定義的上面。
我們來看看上面這個自定義屬性的使用:
[MobileInformation("Apple", 2021)] public class IPhone12 { /*...*/ }
這兒需要註意一下:對於自定義屬性的名字,如果我們采用xxx+Attribute的名稱,則使用時我們可以用短名稱xxx。否則,就需要使用完整的名稱:
public class abc : Attribute { /*...*/ } [abc("Apple", 2021)] public class IPhone12 { /*...*/ }
四、限制屬性
屬性本身也是一個類。所以屬性也可以用屬性來指定和修飾。
在修飾屬性的屬性中,有一個框架中的屬性用的很多,就是AttributeUsage。這個屬性用來限制自定義屬性可以修飾的元素類型:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] public class MobileInformationAttribute : Attribute { /*...*/ }
AttributeTargets是一個枚舉,有很多選項,包括類、接口、方法、構造函數、枚舉、程序集等。
上邊的代碼,我們限定瞭屬性隻用於指定和修飾類和接口。所以,如果用這個屬性來修飾一個字段,編譯器會報錯。
AttributeUsage還允許我們定義從修飾對象繼承的對象,是否也獲得屬性:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = true)] public class MobileInformationAttribute : Attribute { /*...*/ }
以及該屬性是否可以在一個元素上有多個實例:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] public class MobileInformationAttribute : Attribute { /*...*/ }
五、訪問屬性
有瞭屬性,怎麼訪問呢?
框架提供瞭一個方法Attribute.GetCustomAttribute():
var mobileType = typeof(IPhone12); var attributeType = typeof(MobileInformationAttribute); var attribute = (MobileInformationAttribute)Attribute.GetCustomAttribute(mobileType, attributeType); Console.WriteLine($"Mobile is {attribute.brand} {attribute.yearOfProduct}");
六、反射訪問
反射最主要的作用,是用來收集對象的數據,而不是對象本身的數據。這些數據包括對象的類型,以及關於對象成員(包括方法、屬性、構造函數)的信息,和關於特定程序集的信息。此外,還包括存儲在元素屬性中的任何信息。
最簡單的反射,就是GetType()方法。
int myInt = 5; Type type = myInt.GetType(); Console.WriteLine(type);
除此之外,我們還可以使用反射來獲取關於包含給定類型的程序集的信息:
Assembly assembly = typeof(DateTime).Assembly; Console.WriteLine(assembly); Assembly mobileAssembly = typeof(IPhone12).Assembly; Console.WriteLine(mobileAssembly);
關於反射的內容,不展開討論。
這兒說的,是通過反射獲取類中方法的信息:
public class ReflectedClass { public string Property1 { get; set; } public int Add(int first, int second) { return first + second; } } ReflectedClass reflected = new ReflectedClass(); MemberInfo member = reflected.GetType().GetMethod("Add"); Console.WriteLine(member); //Int32 Add(Int32, Int32)
同樣,還可能通過反射獲得關於已定義的屬性的信息,以及關於對象的構造函數的信息:
PropertyInfo property = reflected.GetType().GetProperty("Property1"); Console.WriteLine(property); //System.String Property1 ConstructorInfo constructor = reflected.GetType().GetConstructor(new Type[0]); Console.WriteLine(constructor); //Void .ctor()
七、使用反射創建實例
這個需要用到system.Activator。這是一個非常強大的類,可以從類型創建對象的實例。
來看看這個方法的使用:
ReflectedClass newReflected = new ReflectedClass(); var reflectedType = newReflected.GetType(); object newObject = Activator.CreateInstance(reflectedType); Console.WriteLine(newObject);
八、使用反射處理泛型
使用反射處理泛型會比處理普通類型麻煩一點。
這裡需要知道,Type類上有一個屬性用來標識類型是不是泛型:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 }; Console.WriteLine(numbers.GetType().IsGenericType);
同樣,我們也可以用反射來創建一個泛型的實例:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7 }; Type d = numbers.GetType().GetGenericTypeDefinition(); Type[] typeArgs = new Type[] { typeof(int) }; Type constructed = d.MakeGenericType(typeArgs); object list = Activator.CreateInstance(constructed); Console.WriteLine(list.GetType());
有一點復雜,但可以實現。
九、總結
寫得好像有點亂。
總結一下,屬性將元數據分配給元素,包括類、字段、方法等等。該元數據在構建項目時被編譯,並描述元素,而不是元素的數據。
可以創建從Attribute類繼承的自定義屬性。可以使用AttributeUsage屬性來限制這些屬性的使用位置,並且可以使用反射來獲取屬性數據。
反射是一種技術,允許獲取關於元素(而不是元素本身)的元數據和信息。執行反射的最基本方法是使用GetType()方法,但是也可以使用反射來獲取關於方法、構造函數、字段等的信息。
可以使用反射來創建對象的實例,隻要有瞭對象的類型。同時,使用反射創建泛型對象是可能的,但比較復雜,需要泛型對象的類型以及所有泛型參數的類型。
到此這篇關於一篇文章說通C#的屬性Attribute的文章就介紹到這瞭,更多相關C#屬性Attribute內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- None Found