C#中Property和Attribute的區(qū)別實(shí)例詳解
本文實(shí)例分析了C#中Property和Attribute的區(qū)別。分享給大家供大家參考。具體分析如下:
在C#中有兩個(gè)屬性,分別為Property和Attribute,兩個(gè)的中文意思都有特性、屬性之間,但是用法上卻不一樣,為了區(qū)別,本文暫把Property稱(chēng)為特性,把Attribute稱(chēng)為屬性。
Attribute才是本文的主角,把它稱(chēng)為屬性我覺(jué)得很恰當(dāng)。屬性的意思就是附屬于某種事物上的,用來(lái)說(shuō)明這個(gè)事物的各種特征的一種描述。而Attribute就是干這事的。它允許你將信息與你定義的C#類(lèi)型相關(guān)聯(lián),作為類(lèi)型的標(biāo)注。這些信息是任意的,就是說(shuō),它不是由語(yǔ)言本身決定的,你可以隨意建立和關(guān)聯(lián)任何類(lèi)型的任何信息。你可以作用屬性定義設(shè)計(jì)時(shí)信息和運(yùn)行時(shí)信息,甚至是運(yùn)行時(shí)的行為特征。關(guān)鍵在于這些信息不僅可以被用戶取出來(lái)作為一種類(lèi)型的標(biāo)注,它更可以被編譯器所識(shí)別,作為編譯時(shí)的一種附屬條件參加程序的編譯。
以下部分內(nèi)容及代碼來(lái)源于《C#技術(shù)揭秘》(Inside C# Sencond Edition)
定義屬性:
屬性實(shí)際上是一個(gè)派生自System.Attribute基類(lèi)的類(lèi)。System.Attribute類(lèi)含有幾個(gè)用于訪問(wèn)和檢查自定義屬性的方法。盡管你有權(quán)將任何類(lèi)定義為屬性,但是按照慣例來(lái)說(shuō),從System.Attribute派生類(lèi)是有意義的。示例如下:
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; }
}
}
我們?cè)谶@里添加了不同注冊(cè)表的枚舉、屬性類(lèi)的構(gòu)造器以及兩個(gè)特性(Property)。在定義屬性時(shí)你可以做許許多多的事情,下面我們看看如何在運(yùn)行時(shí)查詢屬性。要想在運(yùn)行時(shí)查詢類(lèi)型或成員所附著的屬性,必須使用反射
查詢類(lèi)屬性:
假設(shè)你希望定義一個(gè)屬性,這個(gè)屬性定義了將在其上創(chuàng)建對(duì)象的遠(yuǎn)程服務(wù)器。如果沒(méi)有這個(gè)屬性,就要把此信息保存在一個(gè)常量中或是一個(gè)應(yīng)用程序的資源文件中。通過(guò)使用屬性,只需用以下方法標(biāo)注出類(lèi)的遠(yuǎn)程服務(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();
}
}
}
運(yùn)行結(jié)果為:
Creat this object on COSETTE。
注意:在這個(gè)例子中的屬性類(lèi)名具有Attribute后綴。但是,當(dāng)我們將此屬性附著給類(lèi)型或成員時(shí)卻不包括Attribute后綴。這是C#語(yǔ)言的設(shè)計(jì)者提供的簡(jiǎn)單方式。當(dāng)編譯器看到一個(gè)屬性被附著給一個(gè)類(lèi)型或成員時(shí),它會(huì)搜索具有指定屬性名的System.Attribute派生類(lèi)。如果編譯器沒(méi)有找到匹配的類(lèi),它就在指定的屬性名后面加上Attribute,然后再進(jìn)行搜索。因此,常見(jiàn)的使用做法是將屬性類(lèi)名定義為以Attribute結(jié)尾,在使用時(shí)忽略名稱(chēng)的這一部分。以下的代碼都采用這種命名方式。
查詢方法屬性:
在下面這個(gè)例子中,我們使用屬性將方法定義為可事務(wù)化的方法,只要存在TransactionableAttribute屬性,代碼就知道具有這個(gè)屬性的方法可以屬于一個(gè)事務(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();
}
}
}
運(yùn)行結(jié)果如下:
Foo is transactionable.
Goo is transactionable.
查詢字段屬性:
假設(shè)有一個(gè)類(lèi)含有一些字段,我們希望將它們的值保存進(jìn)注冊(cè)表。為此,可以使用以枚舉值和字符串為參數(shù)的構(gòu)造器定義一個(gè)屬性,這個(gè)枚舉值代表正確的注冊(cè)表hive,字符串代表注冊(cè)表值名稱(chēng)。在運(yùn)行時(shí)可以查詢字段的注冊(cè)表鍵。
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();
}
}
}
運(yùn)行結(jié)果為:
Foo will be saved in HKEY_CURRENT_USER\\Foo
大家可以看到,用屬性來(lái)標(biāo)注類(lèi)、方法、字段,既可以把用戶的自定義信息附屬在實(shí)體上,又可以在運(yùn)行時(shí)動(dòng)態(tài)的查詢。下面我將講一些C#中默認(rèn)的預(yù)定義屬性,見(jiàn)下表:
預(yù)定義的屬性 有效目標(biāo) 說(shuō)明
AttributeUsage Class 指定另一個(gè)屬性類(lèi)的有效使用方式
CLSCompliant 全部 指出程序元素是否與CLS兼容
Conditional Method 指出如果沒(méi)有定義相關(guān)聯(lián)的字符串,編譯器就可以忽略對(duì)這個(gè)方法的任何調(diào)用
DllImport Method 指定包含外部方法的實(shí)現(xiàn)的DLL位置
STAThread Method(Main) 指出程序的默認(rèn)線程模型為STA
MTAThread Method(Main) 指出程序的默認(rèn)模型為多線程(MTA)
Obsolete 除了Assembly、Module、Parameter和Return 將一個(gè)元素標(biāo)示為不可用,通知用戶此元素將被從未來(lái)的產(chǎn)品
ParamArray Parameter 允許單個(gè)參數(shù)被隱式地當(dāng)作params(數(shù)組)參數(shù)對(duì)待
Serializable Class、Struct、enum、delegate 指定這種類(lèi)型的所有公共和私有字段可以被串行化
NonSerialized Field 應(yīng)用于被標(biāo)示為可串行化的類(lèi)的字段,指出這些字段將不可被串行化
StructLayout Class、struct 指定類(lèi)或結(jié)構(gòu)的數(shù)據(jù)布局的性質(zhì),比如Auto、Explicit或sequential
ThreadStatic Field(靜態(tài)) 實(shí)現(xiàn)線程局部存儲(chǔ)(TLS)。不能跨多個(gè)線程共享給定的靜態(tài)字段,每個(gè)線程擁有這個(gè)靜態(tài)字段的副本
下面介紹幾種常用的屬性
1.[STAThread]和[MTAThread]屬性
class Class1
{
[STAThread]
Static void Main( string[] args )
{
}
}
使用STAThread屬性將程序的默認(rèn)線程模型指定為單線程模型。注意,線程模型只影響使用COM interop的應(yīng)用程序,將這個(gè)屬性應(yīng)用于不使用COM interop的程序?qū)⒉粫?huì)產(chǎn)生任何效果。
2. AttributeUsage屬性
除了用于標(biāo)注常規(guī)C#類(lèi)型的自定義屬性以外,還可以使用AttributeUsage屬性定義你使用這些屬性的方式。文件記錄的AttributeUsage屬性調(diào)用用法如下:
[AttributeUsage( validon , AllowMutiple = allowmutiple , Inherited = inherited )]
Validon參數(shù)是AttributeTargets類(lèi)型的,這個(gè)枚舉值的定義如下:
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決定了可以在單個(gè)字段上使用某個(gè)屬性多少次,在默認(rèn)情況下,所有的屬性都是單次使用的。示例如下:
[AttributeUsage( AttributeTargets.All , AllowMultiple = true )]
public class SomethingAttribute : Attribute
{
public SomethingAttribute( string str )
{
}
}
//如果AllowMultiple = false , 此處會(huì)報(bào)錯(cuò)
[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時(shí),結(jié)果為:
Custom Attribute : def
當(dāng)AllowMultiple被設(shè)置為true時(shí),結(jié)果為:
Custom Attribute : def
Custom Attribute : abc
注意,如果將false傳遞給GetCustomAttributes,它不會(huì)搜索繼承樹(shù),所以你只能得到派生的類(lèi)屬性。
3.Conditional 屬性
你可以將這個(gè)屬性附著于方法,這樣當(dāng)編譯器遇到對(duì)這個(gè)方法調(diào)用時(shí),如果沒(méi)有定義對(duì)應(yīng)的字符串值,編譯器就忽略這個(gè)調(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ā)展,你很可以會(huì)有一些方法不用??梢詫⑺鼈兌紕h除,但是有時(shí)給它們加上適當(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屬性的第二個(gè)參數(shù)設(shè)置為true,當(dāng)調(diào)用時(shí)函數(shù)時(shí)編譯器會(huì)產(chǎn)生一個(gè)錯(cuò)誤。
E:\InsideC#\Code\Chap06\ObsAttrib\ObsAttrib\Class1.cs(20): 'ObsAttrib.SomeClass.OldFunc()' 已過(guò)時(shí): 'Don't use OldFunc, use NewFunc instead'
5. DllImport和StructLayout屬性
DllImport可以讓C#代碼調(diào)用本機(jī)代碼中的函數(shù),C#代碼通過(guò)平臺(tái)調(diào)用(platform invoke)這個(gè)運(yùn)行時(shí)功能調(diào)用它們。
如果你希望運(yùn)行時(shí)環(huán)境將結(jié)構(gòu)從托管代碼正確地編組現(xiàn)非托管代碼(或相反),那么需要為結(jié)構(gòu)的聲明附加屬性。為了使結(jié)構(gòu)參數(shù)可以被正確的編組,必須使用StructLayout屬性聲明它們,指出數(shù)據(jù)應(yīng)該嚴(yán)格地按照聲明中列出的樣子進(jìn)行布局。如果不這么做,數(shù)據(jù)將不能正確地被編組,而應(yīng)用程序可能會(huì)出錯(cuò)。
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)生任何類(lèi)型的C#工程時(shí),會(huì)自動(dòng)的產(chǎn)生一個(gè)AssemblyInfo.cs源代碼文件以及應(yīng)用程序源代碼文件。AssemblyInfo.cs中含有配件中代碼的信息。其中的一些信息純粹是信息,而其它信息使運(yùn)行時(shí)環(huán)境可以確保惟一的命名和版本號(hào),以供重用你的配件的客戶代碼使用。
7. 上下文屬性
.NET柜架還提供了另一種屬性:上下文屬性。上下文屬性提供了一種截取機(jī)制,可以在類(lèi)的實(shí)例化和方法調(diào)用之前和之后進(jìn)行處理。這種功能用于對(duì)象遠(yuǎn)程調(diào)用,它是從基于COM的系統(tǒng)所用的COM+組件服務(wù)和Microsoft Transaction Services(MTS)。
希望本文所述對(duì)大家的C#程序設(shè)計(jì)有所幫助。
相關(guān)文章
C#實(shí)現(xiàn)簡(jiǎn)單合并word文檔的方法
這篇文章主要介紹了C#實(shí)現(xiàn)簡(jiǎn)單合并word文檔的方法,涉及C#針對(duì)word文檔的讀取、插入、保存等技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-09-09
C#利用OLEDB實(shí)現(xiàn)將DataTable寫(xiě)入Excel文件中
這篇文章主要為大家詳細(xì)介紹了C#如何利用OLEDB實(shí)現(xiàn)將DataTable寫(xiě)入Excel文件中,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,需要的可以參考一下2023-02-02
C#中序列化實(shí)現(xiàn)深拷貝,實(shí)現(xiàn)DataGridView初始化刷新的方法
下面小編就為大家?guī)?lái)一篇C#中序列化實(shí)現(xiàn)深拷貝,實(shí)現(xiàn)DataGridView初始化刷新的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02
C#實(shí)現(xiàn)將PDF轉(zhuǎn)為Excel的方法詳解
通常,PDF格式的文檔能支持的編輯功能不如office文檔多,針對(duì)PDF文檔里面有表格數(shù)據(jù)的,如果想要編輯表格里面的數(shù)據(jù),可以將該P(yáng)DF文檔轉(zhuǎn)為Excel格式。本文將介紹如何利用C#實(shí)現(xiàn)PDF轉(zhuǎn)Excel,需要的可以參考一下2022-04-04
C#通過(guò)屬性名字符串獲取、設(shè)置對(duì)象屬性值操作示例
這篇文章主要介紹了C#通過(guò)屬性名字符串獲取、設(shè)置對(duì)象屬性值操作,結(jié)合實(shí)例形式總結(jié)分析了C#通過(guò)反射獲取對(duì)象屬性值并設(shè)置屬性值,獲取對(duì)象的所有屬性名稱(chēng)及類(lèi)型等相關(guān)操作技巧,需要的朋友可以參考下2020-03-03
C#基于SerialPort類(lèi)實(shí)現(xiàn)串口通訊詳解
這篇文章主要為大家詳細(xì)介紹了C#基于SerialPort類(lèi)實(shí)現(xiàn)串口通訊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01

