C#中Property和Attribute的區(qū)別實例詳解
本文實例分析了C#中Property和Attribute的區(qū)別。分享給大家供大家參考。具體分析如下:
在C#中有兩個屬性,分別為Property和Attribute,兩個的中文意思都有特性、屬性之間,但是用法上卻不一樣,為了區(qū)別,本文暫把Property稱為特性,把Attribute稱為屬性。
Attribute才是本文的主角,把它稱為屬性我覺得很恰當(dāng)。屬性的意思就是附屬于某種事物上的,用來說明這個事物的各種特征的一種描述。而Attribute就是干這事的。它允許你將信息與你定義的C#類型相關(guān)聯(lián),作為類型的標(biāo)注。這些信息是任意的,就是說,它不是由語言本身決定的,你可以隨意建立和關(guān)聯(lián)任何類型的任何信息。你可以作用屬性定義設(shè)計時信息和運行時信息,甚至是運行時的行為特征。關(guān)鍵在于這些信息不僅可以被用戶取出來作為一種類型的標(biāo)注,它更可以被編譯器所識別,作為編譯時的一種附屬條件參加程序的編譯。
以下部分內(nèi)容及代碼來源于《C#技術(shù)揭秘》(Inside C# Sencond Edition)
定義屬性:
屬性實際上是一個派生自System.Attribute基類的類。System.Attribute類含有幾個用于訪問和檢查自定義屬性的方法。盡管你有權(quán)將任何類定義為屬性,但是按照慣例來說,從System.Attribute派生類是有意義的。示例如下:
public enum RegHives { HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG } public class RegKeyAttribute : Attribute { public RegKeyAttribute(RegHives Hive, String ValueName) { this.Hive = Hive; this.ValueName = ValueName; } protected RegHives hive; public RegHives Hive { get { return hive; } set { hive = value; } } protected String valueName; public String ValueName { get { return valueName; } set { valueName = value; } } }
我們在這里添加了不同注冊表的枚舉、屬性類的構(gòu)造器以及兩個特性(Property)。在定義屬性時你可以做許許多多的事情,下面我們看看如何在運行時查詢屬性。要想在運行時查詢類型或成員所附著的屬性,必須使用反射
查詢類屬性:
假設(shè)你希望定義一個屬性,這個屬性定義了將在其上創(chuàng)建對象的遠程服務(wù)器。如果沒有這個屬性,就要把此信息保存在一個常量中或是一個應(yīng)用程序的資源文件中。通過使用屬性,只需用以下方法標(biāo)注出類的遠程服務(wù)器名即可:
using System; namespace QueryAttribs { public enum RemoteServers { JEANVALJEAN, JAVERT, COSETTE } public class RemoteObjectAttribute : Attribute { public RemoteObjectAttribute(RemoteServers Server) { this.server = Server; } protected RemoteServers server; public string Server { get { return RemoteServers.GetName( typeof(RemoteServers), this.server); } } } [RemoteObject(RemoteServers.COSETTE)] class MyRemotableClass { } class Test { [STAThread] static void Main(string[] args) { Type type = typeof(MyRemotableClass); foreach (Attribute attr in type.GetCustomAttributes(true)) { RemoteObjectAttribute remoteAttr = attr as RemoteObjectAttribute; if (null != remoteAttr) { Console.WriteLine( "Create this object on {0}.", remoteAttr.Server); } } Console.ReadLine(); } } }
運行結(jié)果為:
Creat this object on COSETTE。
注意:在這個例子中的屬性類名具有Attribute后綴。但是,當(dāng)我們將此屬性附著給類型或成員時卻不包括Attribute后綴。這是C#語言的設(shè)計者提供的簡單方式。當(dāng)編譯器看到一個屬性被附著給一個類型或成員時,它會搜索具有指定屬性名的System.Attribute派生類。如果編譯器沒有找到匹配的類,它就在指定的屬性名后面加上Attribute,然后再進行搜索。因此,常見的使用做法是將屬性類名定義為以Attribute結(jié)尾,在使用時忽略名稱的這一部分。以下的代碼都采用這種命名方式。
查詢方法屬性:
在下面這個例子中,我們使用屬性將方法定義為可事務(wù)化的方法,只要存在TransactionableAttribute屬性,代碼就知道具有這個屬性的方法可以屬于一個事務(wù)。
using System; using System.Reflection; namespace MethodAttribs { public class TransactionableAttribute : Attribute { public TransactionableAttribute() { } } class SomeClass { [Transactionable] public void Foo() {} public void Bar() {} [Transactionable] public void Goo() {} } class Test { [STAThread] static void Main(string[] args) { Type type = Type.GetType("MethodAttribs.SomeClass"); foreach (MethodInfo method in type.GetMethods()) { foreach (Attribute attr in method.GetCustomAttributes(true)) { if (attr is TransactionableAttribute) { Console.WriteLine( "{0} is transactionable.", method.Name); } } } Console.ReadLine(); } } }
運行結(jié)果如下:
Foo is transactionable.
Goo is transactionable.
查詢字段屬性:
假設(shè)有一個類含有一些字段,我們希望將它們的值保存進注冊表。為此,可以使用以枚舉值和字符串為參數(shù)的構(gòu)造器定義一個屬性,這個枚舉值代表正確的注冊表hive,字符串代表注冊表值名稱。在運行時可以查詢字段的注冊表鍵。
using System; using System.Reflection; namespace FieldAttribs { public enum RegHives { HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG } public class RegKeyAttribute : Attribute { public RegKeyAttribute(RegHives Hive, String ValueName) { this.Hive = Hive; this.ValueName = ValueName; } protected RegHives hive; public RegHives Hive { get { return hive; } set { hive = value; } } protected String valueName; public String ValueName { get { return valueName; } set { valueName = value; } } } class SomeClass { [RegKey(RegHives.HKEY_CURRENT_USER, "Foo")] public int Foo; public int Bar; } class Test { [STAThread] static void Main(string[] args) { Type type = Type.GetType("FieldAttribs.SomeClass"); foreach (FieldInfo field in type.GetFields()) { foreach (Attribute attr in field.GetCustomAttributes(true)) { RegKeyAttribute rka = attr as RegKeyAttribute; if (null != rka) { Console.WriteLine( "{0} will be saved in" + " {1}\\\\{2}", field.Name, rka.Hive, rka.ValueName); } } } Console.ReadLine(); } } }
運行結(jié)果為:
Foo will be saved in HKEY_CURRENT_USER\\Foo
大家可以看到,用屬性來標(biāo)注類、方法、字段,既可以把用戶的自定義信息附屬在實體上,又可以在運行時動態(tài)的查詢。下面我將講一些C#中默認(rèn)的預(yù)定義屬性,見下表:
預(yù)定義的屬性 有效目標(biāo) 說明
AttributeUsage Class 指定另一個屬性類的有效使用方式
CLSCompliant 全部 指出程序元素是否與CLS兼容
Conditional Method 指出如果沒有定義相關(guān)聯(lián)的字符串,編譯器就可以忽略對這個方法的任何調(diào)用
DllImport Method 指定包含外部方法的實現(xiàn)的DLL位置
STAThread Method(Main) 指出程序的默認(rèn)線程模型為STA
MTAThread Method(Main) 指出程序的默認(rèn)模型為多線程(MTA)
Obsolete 除了Assembly、Module、Parameter和Return 將一個元素標(biāo)示為不可用,通知用戶此元素將被從未來的產(chǎn)品
ParamArray Parameter 允許單個參數(shù)被隱式地當(dāng)作params(數(shù)組)參數(shù)對待
Serializable Class、Struct、enum、delegate 指定這種類型的所有公共和私有字段可以被串行化
NonSerialized Field 應(yīng)用于被標(biāo)示為可串行化的類的字段,指出這些字段將不可被串行化
StructLayout Class、struct 指定類或結(jié)構(gòu)的數(shù)據(jù)布局的性質(zhì),比如Auto、Explicit或sequential
ThreadStatic Field(靜態(tài)) 實現(xiàn)線程局部存儲(TLS)。不能跨多個線程共享給定的靜態(tài)字段,每個線程擁有這個靜態(tài)字段的副本
下面介紹幾種常用的屬性
1.[STAThread]和[MTAThread]屬性
class Class1 { [STAThread] Static void Main( string[] args ) { } }
使用STAThread屬性將程序的默認(rèn)線程模型指定為單線程模型。注意,線程模型只影響使用COM interop的應(yīng)用程序,將這個屬性應(yīng)用于不使用COM interop的程序?qū)⒉粫a(chǎn)生任何效果。
2. AttributeUsage屬性
除了用于標(biāo)注常規(guī)C#類型的自定義屬性以外,還可以使用AttributeUsage屬性定義你使用這些屬性的方式。文件記錄的AttributeUsage屬性調(diào)用用法如下:
[AttributeUsage( validon , AllowMutiple = allowmutiple , Inherited = inherited )] Validon參數(shù)是AttributeTargets類型的,這個枚舉值的定義如下: public enum AttributeTargets { Assembly = 0x0001, Module = 0x0002, Class = 0x0004, Struct = 0x0008, Enum = 0x0010, Constructor = 0x0020, Method = 0x0040, Property = 0x0080, Field = 0x0100, Event = 0x200, Interface = 0x400, Parameter = 0x800, Delegate = 0x1000, All = Assembly | Module | Class | Struct | Enum | Constructor| Method | Property| Filed| Event| Interface | Parameter | Deleagte , ClassMembers = | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface }
AllowMultiple決定了可以在單個字段上使用某個屬性多少次,在默認(rèn)情況下,所有的屬性都是單次使用的。示例如下:
[AttributeUsage( AttributeTargets.All , AllowMultiple = true )] public class SomethingAttribute : Attribute { public SomethingAttribute( string str ) { } } //如果AllowMultiple = false , 此處會報錯 [Something(“abc”)] [Something(“def”)] class Myclass { }
Inherited參數(shù)是繼承的標(biāo)志,它指出屬性是否可以被繼承。默認(rèn)是false。
Inherited AllowMultiple 結(jié)果
true false 派生的屬性覆蓋基屬性
true false 派生的屬性和基屬性共存
代碼示例:
using System; using System.Reflection; namespace AttribInheritance { [AttributeUsage( AttributeTargets.All, AllowMultiple=true, // AllowMultiple=false, Inherited=true )] public class SomethingAttribute : Attribute { private string name; public string Name { get { return name; } set { name = value; } } public SomethingAttribute(string str) { this.name = str; } } [Something("abc")] class MyClass { } [Something("def")] class Another : MyClass { } class Test { [STAThread] static void Main(string[] args) { Type type = Type.GetType("AttribInheritance.Another"); foreach (Attribute attr in type.GetCustomAttributes(true)) // type.GetCustomAttributes(false)) { SomethingAttribute sa = attr as SomethingAttribute; if (null != sa) { Console.WriteLine( "Custom Attribute: {0}", sa.Name); } } } } }
當(dāng)AllowMultiple被設(shè)置為false時,結(jié)果為:
Custom Attribute : def
當(dāng)AllowMultiple被設(shè)置為true時,結(jié)果為:
Custom Attribute : def
Custom Attribute : abc
注意,如果將false傳遞給GetCustomAttributes,它不會搜索繼承樹,所以你只能得到派生的類屬性。
3.Conditional 屬性
你可以將這個屬性附著于方法,這樣當(dāng)編譯器遇到對這個方法調(diào)用時,如果沒有定義對應(yīng)的字符串值,編譯器就忽略這個調(diào)用。例如,以下方法是否被編譯取決于是否定義了字符串“DEGUG”:
[Condition(“DEBUG”) ] public void SomeDebugFunc() { Console.WriteLine(“SomeDebugFunc”); } using System; using System.Diagnostics; namespace CondAttrib { class Thing { private string name; public Thing(string name) { this.name = name; #if DEBUG SomeDebugFunc(); #else SomeFunc(); #endif } public void SomeFunc() { Console.WriteLine("SomeFunc"); } [Conditional("DEBUG")] [Conditional("ANDREW")] public void SomeDebugFunc() { Console.WriteLine("SomeDebugFunc"); } } public class Class1 { [STAThread] static void Main(string[] args) { Thing t = new Thing("T1"); } } }
4. Obsolete 屬性
隨著代碼不斷的發(fā)展,你很可以會有一些方法不用??梢詫⑺鼈兌紕h除,但是有時給它們加上適當(dāng)?shù)臉?biāo)注比刪除它們更合適,例如:
using System; namespace ObsAttrib { class SomeClass { [Obsolete("Don't use OldFunc, use NewFunc instead", true)] public void OldFunc( ) { Console.WriteLine("Oops"); } public void NewFunc( ) { Console.WriteLine("Cool"); } } class Class1 { [STAThread] static void Main(string[] args) { SomeClass sc = new SomeClass(); sc.NewFunc(); // sc.OldFunc(); // compiler error } } }
我們將Obsolete屬性的第二個參數(shù)設(shè)置為true,當(dāng)調(diào)用時函數(shù)時編譯器會產(chǎn)生一個錯誤。
E:\InsideC#\Code\Chap06\ObsAttrib\ObsAttrib\Class1.cs(20): 'ObsAttrib.SomeClass.OldFunc()' 已過時: 'Don't use OldFunc, use NewFunc instead'
5. DllImport和StructLayout屬性
DllImport可以讓C#代碼調(diào)用本機代碼中的函數(shù),C#代碼通過平臺調(diào)用(platform invoke)這個運行時功能調(diào)用它們。
如果你希望運行時環(huán)境將結(jié)構(gòu)從托管代碼正確地編組現(xiàn)非托管代碼(或相反),那么需要為結(jié)構(gòu)的聲明附加屬性。為了使結(jié)構(gòu)參數(shù)可以被正確的編組,必須使用StructLayout屬性聲明它們,指出數(shù)據(jù)應(yīng)該嚴(yán)格地按照聲明中列出的樣子進行布局。如果不這么做,數(shù)據(jù)將不能正確地被編組,而應(yīng)用程序可能會出錯。
using System; using System.Runtime.InteropServices; // for DllImport namespace nativeDLL { public class Test { // [DllImport ("user32.dll")] // all the defaults are OK [DllImport("user32", EntryPoint="MessageBoxA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)] public static extern int MessageBoxA ( int h, string m, string c, int type); [StructLayout(LayoutKind.Sequential)] public class SystemTime { public ushort wYear; public ushort wMonth; public ushort wDayOfWeek; public ushort wDay; public ushort wHour; public ushort wMinute; public ushort wSecond; public ushort wMilliseconds; } [DllImport ("kernel32.dll")] public static extern void GetLocalTime(SystemTime st); [STAThread] public static void Main(string[] args) { MessageBoxA(0, "Hello World", "nativeDLL", 0); SystemTime st = new SystemTime(); GetLocalTime(st); string s = String.Format("date: {0}-{1}-{2}", st.wMonth, st.wDay, st.wYear); string t = String.Format("time: {0}:{1}:{2}", st.wHour, st.wMinute, st.wSecond); string u = s + ", " + t; MessageBoxA(0, u, "Now", 0); } } }
6. 配件屬性
當(dāng)使用.NET產(chǎn)生任何類型的C#工程時,會自動的產(chǎn)生一個AssemblyInfo.cs源代碼文件以及應(yīng)用程序源代碼文件。AssemblyInfo.cs中含有配件中代碼的信息。其中的一些信息純粹是信息,而其它信息使運行時環(huán)境可以確保惟一的命名和版本號,以供重用你的配件的客戶代碼使用。
7. 上下文屬性
.NET柜架還提供了另一種屬性:上下文屬性。上下文屬性提供了一種截取機制,可以在類的實例化和方法調(diào)用之前和之后進行處理。這種功能用于對象遠程調(diào)用,它是從基于COM的系統(tǒng)所用的COM+組件服務(wù)和Microsoft Transaction Services(MTS)。
希望本文所述對大家的C#程序設(shè)計有所幫助。
相關(guān)文章
C#利用OLEDB實現(xiàn)將DataTable寫入Excel文件中
這篇文章主要為大家詳細介紹了C#如何利用OLEDB實現(xiàn)將DataTable寫入Excel文件中,文中的示例代碼簡潔易懂,具有一定的借鑒價值,需要的可以參考一下2023-02-02C#中序列化實現(xiàn)深拷貝,實現(xiàn)DataGridView初始化刷新的方法
下面小編就為大家?guī)硪黄狢#中序列化實現(xiàn)深拷貝,實現(xiàn)DataGridView初始化刷新的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02C#實現(xiàn)將PDF轉(zhuǎn)為Excel的方法詳解
通常,PDF格式的文檔能支持的編輯功能不如office文檔多,針對PDF文檔里面有表格數(shù)據(jù)的,如果想要編輯表格里面的數(shù)據(jù),可以將該PDF文檔轉(zhuǎn)為Excel格式。本文將介紹如何利用C#實現(xiàn)PDF轉(zhuǎn)Excel,需要的可以參考一下2022-04-04