一文说通C#的属性Attribute
屬性Attributes這個東西,用好了可以省N多代碼。
?
一、屬性
屬性Attributes在C#中很常用,但事實上很多人對這個東西又很陌生。
從概念上講,屬性提供的是將元數(shù)據(jù)關(guān)系到元素的一種方式。
?
屬性使用的樣子,應(yīng)該都見過:
[Flags]?//Attribute public?enum?DayOfWeek {Sunday?=?1,Monday?=?2,Tuesday?=?4,Wednesday?=?8,Thursday?=?16,Friday?=?32,Saturday?=?64 }代碼中,Flags就是一個屬性。
通常,屬性會放在類、字段、方法等定義的上面,用來指定特定的內(nèi)容。
?
.Net Framework框架提供了一些屬性。像常見的Serializable,用來告訴編譯器當(dāng)前的類可以序列化成JSON或XML:
[Serializable] public?class?SerializableClass?{?/*...*/?}?
需要注意的是,屬性在編譯時會嵌入到程序集中。這樣,我們可以使用反射來獲得相應(yīng)的屬性值。
二、自定義屬性
自定義屬性用處很大,算是我自己比較常用的一個技術(shù)。
自定義屬性需要從System.Attribute抽象類來繼承。
?
想象一個場景。我們在構(gòu)建一個手機類。我們需要一個屬性來表示手機一些信息,比方口牌和生產(chǎn)年份:
public?class?MobileInformationAttribute?:?Attribute {public?string?brand?{?get;?set;?}public?int?yearOfProduct?{?get;?set;?}public?MobileInformationAttribute(string?Brand,?int?YearOfProduct){brand?=?Brand;yearOfProduct?=?YearOfProduct;} }我們會注意到:屬性是一個類,和其它類一樣,擁有字段、方法、構(gòu)造函數(shù)和其它成員。
三、使用屬性
前面說了,屬性可以放在類、字段、方法等定義的上面。
我們來看看上面這個自定義屬性的使用:
[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是一個枚舉,有很多選項,包括類、接口、方法、構(gòu)造函數(shù)、枚舉、程序集等。
上邊的代碼,我們限定了屬性只用于指定和修飾類和接口。所以,如果用這個屬性來修飾一個字段,編譯器會報錯。
?
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}");六、反射訪問
反射最主要的作用,是用來收集對象的數(shù)據(jù),而不是對象本身的數(shù)據(jù)。這些數(shù)據(jù)包括對象的類型,以及關(guān)于對象成員(包括方法、屬性、構(gòu)造函數(shù))的信息,和關(guān)于特定程序集的信息。此外,還包括存儲在元素屬性中的任何信息。
最簡單的反射,就是GetType()方法。
int?myInt?=?5; Type?type?=?myInt.GetType(); Console.WriteLine(type);除此之外,我們還可以使用反射來獲取關(guān)于包含給定類型的程序集的信息:
Assembly?assembly?=?typeof(DateTime).Assembly; Console.WriteLine(assembly);Assembly?mobileAssembly?=?typeof(IPhone12).Assembly; Console.WriteLine(mobileAssembly);關(guān)于反射的內(nèi)容,不展開討論。
這兒說的,是通過反射獲取類中方法的信息:
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)同樣,還可能通過反射獲得關(guān)于已定義的屬性的信息,以及關(guān)于對象的構(gòu)造函數(shù)的信息:
PropertyInfo?property?=?reflected.GetType().GetProperty("Property1"); Console.WriteLine(property);?//System.String?Property1ConstructorInfo?constructor?=?reflected.GetType().GetConstructor(new?Type[0]); Console.WriteLine(constructor);?//Void?.ctor()七、使用反射創(chuàng)建實例
這個需要用到system.Activator。這是一個非常強大的類,可以從類型創(chuàng)建對象的實例。
來看看這個方法的使用:
ReflectedClass?newReflected?=?new?ReflectedClass();var?reflectedType?=?newReflected.GetType();object?newObject?=?Activator.CreateInstance(reflectedType); Console.WriteLine(newObject);八、使用反射處理泛型
使用反射處理泛型會比處理普通類型麻煩一點。
這里需要知道,Type類上有一個屬性用來標(biāo)識類型是不是泛型:
List<int>?numbers?=?new?List<int>?{?1,?2,?3,?4,?5,?6,?7?}; Console.WriteLine(numbers.GetType().IsGenericType);同樣,我們也可以用反射來創(chuàng)建一個泛型的實例:
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());有一點復(fù)雜,但可以實現(xiàn)。
九、總結(jié)
寫得好像有點亂。
總結(jié)一下,屬性將元數(shù)據(jù)分配給元素,包括類、字段、方法等等。該元數(shù)據(jù)在構(gòu)建項目時被編譯,并描述元素,而不是元素的數(shù)據(jù)。
可以創(chuàng)建從Attribute類繼承的自定義屬性。可以使用AttributeUsage屬性來限制這些屬性的使用位置,并且可以使用反射來獲取屬性數(shù)據(jù)。
反射是一種技術(shù),允許獲取關(guān)于元素(而不是元素本身)的元數(shù)據(jù)和信息。執(zhí)行反射的最基本方法是使用GetType()方法,但是也可以使用反射來獲取關(guān)于方法、構(gòu)造函數(shù)、字段等的信息。
可以使用反射來創(chuàng)建對象的實例,只要有了對象的類型。同時,使用反射創(chuàng)建泛型對象是可能的,但比較復(fù)雜,需要泛型對象的類型以及所有泛型參數(shù)的類型。
喜歡就來個三連,讓更多人因你而受益
總結(jié)
以上是生活随笔為你收集整理的一文说通C#的属性Attribute的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NET问答: Find() 和 Wher
- 下一篇: Polly-故障处理和弹性应对很有一手