日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入浅出Attribute (转载)

發布時間:2024/1/17 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入浅出Attribute (转载) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為什么80%的碼農都做不了架構師?>>> ??

原文地址:http://blog.csdn.net/FantasiaX/article/details/1627694

正文:

什么是Attribute?Attribute是干什么使的?Attribute與Property到底有什么區別?……

長久以來,這些問題一直困擾著并不怎么廣大的C#初學者。原因大概有兩個,一是Attribute平時不怎么常用(沒用慣怎么可能常用嗎!)二是這個家伙不太好翻譯——它與Property這個詞意義相近,都有“屬性”這個詞條(而且在HTML語言中,Attribute也的確與C#面向對象概念中的Property意思一致),因此很多譯者,特別是C#剛剛出來那陣子,拿捏不好應該怎么翻譯,搞的C#初學者一遇到“屬性”就發懵、一遇到“Attribute”就發怵?,F在情況還算比較明朗了,Attribute一般譯作“特性”,Property仍然譯為“屬性”。

今天,先讓我們來學習一下到底什么是Attribute并體驗一下Attribute的威力,然后讓我們徹底澄清Attribute與Property之間的區別。

什么是Attribute:

大多數書籍都喜歡講——“Attribute是一種可由用戶自由定義的修飾符(Modifier),可以用來修飾各種需要被修飾的目標”——如此晦澀的言辭,怎么可能讓新手一下子明白呢?(不過,等你看完這篇文章、成為“老手”之后,你會發現這句話說得還是挺有道理的。)況且,修飾符(比如private、public、static、ref、out等等)都是C#語言本身的關鍵字,而Attribute看起來又與語言本身不著邊際。God!Attribute到底是個什么東東呢?

其實特別簡單——Attribute就是一種“附著物”——就像牡蠣吸附在船底或礁石上一樣。這些附著物的作用是為它們的附著體追加上一些額外的信息(這些信息就保存在附著物的體內)——比如“這個類是我寫的”或者“這個函數以前出過問題”等等。

你可能會問:這跟注釋有什么區別呢?

當然有區別啦!注釋是對程序源代碼的一種說明,主要目的是給人看的,在程序被編譯的時候會被編譯器所丟棄,因此,它絲毫不會影響到程序的執行。而Attribute是程序代碼的一部分,不但不會被編譯器丟棄,而且還會被編譯器編譯進程序集(Assembly)的元數據(Metadata)里,在程序運行的時候,你隨時可以從元數據里提取出這些附加信息來決策程序的運行。

口說無憑,舉個例子你馬上就會明白了——讓我們來考慮這樣一種情況:

有一個類,由兩個程序員——小張和小李——共同維護。這個類是在項目中起一個“工具包”(Utilities)的作用(就像.NET Framework中的Math類一樣),里面含了幾十個靜態方法(也就是用static修飾過的函數啦)。這些靜態方法中,一半是小張寫的、一半是小李寫的;在項目的測試中,還有一些靜態方法曾經出過bug,當然后來又被修正過了。這樣,我們就可以把這些方面劃分成這樣幾類:

我們分類的目的主要是在測試的時候可以按不同的類別進行測試、獲取不同的效果——比如統計兩個人的工作量或者對曾經出過bug的方法進行回歸測試。

如果不使用Attribute,為了區分這四類靜態方法,我們有兩種方法:

1. 把這些信息體現在方法的名稱中,看起來就像這樣——

//...

public static void Li_Buged_Method_1(double arg1, double arg2) { /*...*/}

public static void Li_NoBug_Method_2(double arg1, double arg2) { /*...*/}

public static void Zhang_Buged_Method_3(double arg1, double arg2) { /*...*/}

public static void Zhang_NoBug_Method_4(double arg1, double arg2) { /*...*/}

//...

很顯然,這樣是行不通的,因為這樣會在方法命名中遺留很多與程序邏輯本身無關的“垃圾信息”。舉個極端點兒的例子:如果小張和小李調走了,由小趙和小孫接手他們的工作,后來小趙和小孫也調走了,由小劉和小王接手維護工作……舊方法的名字不敢改,新方法的名字要用新姓氏命名,時間一長,這個類就跟《百家姓》沒什么區別了:p

2. 另一種方法是為每個方法加注釋,看起來會是這樣——

//...

public static void Method_1(double arg1, double arg2) { /*...*/} // Created By Li, Buged

public static void Method_2(double arg1, double arg2) { /*...*/} // Created By Li, NoBug

public static void Method_3(double arg1, double arg2) { /*...*/} // Created By Zhang, Buged

public static void Method_4(double arg1, double arg2) { /*...*/} // Created By Zhang, NoBug

//...

這樣做的好處是清除了代碼中的“垃圾信息”,但情況并沒有好到哪兒去。為了統計程序員的工作量,你還得一邊看注釋一邊計數,為了把出過bug和沒出過bug的方法分開跑,你要在執行的時候不停地把這個方法注釋掉(在調用前加//,取消它的執行)、為那個方法取消注釋……如果是幾十個方法還好辦,如果是幾千個呢?(別不相信,我在MSN測試組里呆著的時候,一組方法就有一千四百多個呢!)之所以出現這個問題,根本原因是注釋會被編譯器拋棄,因此在執行期注釋絲毫幫不上我們的忙。

難道我們沒有別的辦法了嗎?峰回路轉,Attribute登場!

Attribute范例

請編譯運行下面這個程序:

//====水之真諦====//
//上善若水,潤物無聲//
/* http://blog.csdn.net/FantasiaX? */
//#define NOBUG
#define BUGED // C#的宏定義必須出現在所有代碼之前。當前我們只讓BUGED宏有效。
//#define LI
//#define ZHANG
using System;
using System.Diagnostics; // 注意:這是為了使用包含在此名稱空間中的ConditionalAttribute特性
namespace AttributeSample
{
class ToolKit
{
[ConditionalAttribute("LI")] // Attribute名稱的長記法
[ConditionalAttribute("BUGED")]
public static void Method1() { Console.WriteLine("Created By Li, Buged."); }
[ConditionalAttribute("LI")]
[ConditionalAttribute("NOBUG")]
public static void Method2() { Console.WriteLine("Created By Li, NoBug."); }
[Conditional("ZHANG")] // Attribute名稱的短記法
[Conditional("BUGED")]
public static void Method3() { Console.WriteLine("Created By Zhang, Buged."); }
[Conditional("ZHANG")]
[Conditional("NOBUG")]
public static void Method4() { Console.WriteLine("Created By Zhang, NoBug."); }
}
class Program
{
static void Main(string[] args)
{
// 雖然方法都被調用了,但只有符合條件的才會被執行。
ToolKit.Method1();
ToolKit.Method2();
ToolKit.Method3();
ToolKit.Method4();
}
}
}

執行結果:

實例分析:

1. 在本例中,我們使用了ConditionalAttribute這個Attribute,它被包含在System.Diagnostics名稱空間中。顯然,它多半時間是用來做程序調試與診斷的。

2. 與ConditionalAttribute相關的是一組C#宏,它們看起來與C語言的宏別無二致,位置必需出現在所有C#代碼之前。顧名思義,ConditionalAttribute是用來判斷條件的,而這組宏就是將被ConditionalAttribute所判斷的條件。凡被ConditionalAttribute“附著”了的方法,只有滿足了條件才會執行。

3. 就像船底上可以附著很多牡蠣一樣,一個方法上也可以附著多個ConditionalAttribute的實例。把Attribute附著在目標上的書寫格式很簡單——用方括號把Attribute一括就行了,后面緊接著寫Attribute的附著體就行了。當多個Attribute附著在同一個目標上時,把這些Attribute的方括號一個挨一個地書寫就行了(或者是在一對方括號中書寫多個Attribute),而且不必在乎它們的順序。

4. 在使用Attribute的時候,有“長記法”和“短記法”兩種,請君自便J

由上面的第3和第4條我們可以推出,以下四種Attribute的使用方式是完全等價的:

[ConditionalAttribute("LI")] // 長記法

[ConditionalAttribute("NOBUG")]

public static void Method2() { Console.WriteLine("Created By Li, NoBug."); }

[Conditional("LI")] // 短記法

[Conditional("NOBUG")]

public static void Method2() { Console.WriteLine("Created By Li, NoBug."); }

[Conditional("NOBUG")] // 換序

[Conditional("LI")]

public static void Method2() { Console.WriteLine("Created By Li, NoBug."); }

[Conditional("NOBUG"), Conditional("LI")] // 單括號疊加

public static void Method2() { Console.WriteLine("Created By Li, NoBug."); }

當我們對Attribute的用途有所了解后,我們就可以向Attribute的本質進發了!

TO BE CONTINUE

請關注:

  • 《深入淺出Attribute》(中)——Attribute的本質
  • 《深入淺出Attribute》(下)——Attribute V.S. Property

法律聲明:本文章受到知識產權法保護,任何單位或個人若需要轉載此文,必需保證文章的完整性(未經作者許可的任何刪節或改動將視為侵權行為)。若您需要轉載,請務必注明文章出處為CSDN以保障網站的權益;請務必注明文章作者為劉鐵猛(http://blog.csdn.net/FantasiaX),并向liutm@beyondsoft.com發送郵件,標明文章位置及用途。轉載時請將此法律聲明一并轉載,謝謝!

深入淺出Attribute(中)——Attribute本質論

CSDN 劉鐵猛
2007-06-04

小序:

上篇里,我們把Attribute“粘”在類的成員方法上show了一把,讓Attribute跟大家混了個臉兒熟。中篇里,我們將探討“究竟什么是Attribute”和“如何創建及使用Attribute”這兩個問題。

準備好了嗎?Let’s go!

正文:

從上篇里我們可以看到,Attribute似乎總跟public、static這些關鍵字(Keyword)出現在一起。莫非使用了Attribute就相當于定義了新的修飾符(Modifier)嗎?讓我們來一窺究竟!

先把下面這個例子編譯出來:

#define OK
using System;
using System.Diagnostics;
namespace Sample
{
class Program
{
[Conditional("OK")]
public static void TargetMethod()
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("/t=<水之真諦>=/nhttp://blog.csdn.net/FantasiaX/n/n");
}
static void Main(string[] args)
{
TargetMethod();
}
}
}

毋庸置疑,它的運行結果會是這樣:

接下來,讓我們把編譯出的結果(.EXE文件)用“微軟中間語言反編譯器”打開,查看存儲在程序集(Assembly,這在個例子中就是這個.EXE文件)中的中間語言代碼(中間語言也就是我們常說的通用語言)。

如果你認為反匯編是件很神秘的事情,那你可就錯了!比起x86匯編語言來,對.NET程序集的反匯編要簡單得多——甚至可以說是與C#語言一一對應:

嚴格地來說,用來形成上圖中樹狀結構的代碼并不是程序集中的中間語言,而主要是元數據(Metadata)的功勞。包含在程序集中的元數據記錄了這個程序集里有多少個namespace、多少個類、類里有什么成員、成員的訪問級別是什么……而且,元數據是以文本(也就是Unicode字符)形式存在的,使用.NET的反射(Reflection)技術,很容易就能把它們讀取出來并形成各種各樣的漂亮視圖——上面的樹狀圖、VS里的Object Browser視圖和自動代碼提示功能,都是元數據與反射技術結合的產物。一個程序集(.EXE或.DLL)能夠使用包含在自己體內的元數據來完整地說明自己,而不必像C/C++那樣帶著一大捆頭文件,這就叫作“自包含性”或“自描述性”。

扯的有點兒遠了——讓我們回到正題,雙擊反編譯器中的TargetMethod:void()。這回彈出窗口里顯示的內容是真正的微軟中間語言代碼了。這些代碼也都是文本形式的,需要經過.NET的“虛擬機”再編譯后才能被CPU所執行。順便說一句:VB.NET代碼也會編譯成這樣的中間代碼,所以,.NET平臺上所有語言的編譯結果都是通用的。換句話說,你用C#編寫了一個組件,把它編譯成一個DLL文件并交給VB.NET程序員,VB.NET程序員可以直接使用,絲毫不必有任何擔心J

今天我們不打算研究中間語言的編譯和執行,主要是打算通過中間語言對一些被C#語言所掩蓋的事實一窺究竟。

仔細觀察中間代碼之后,Attribute變得了無秘密!圖中藍色箭頭所指處是兩個“真正的”修飾符——Attribute并沒有出現在這里。而在紅色箭頭所標識的位置,我們可以清楚地看出——這分明是在調用mscorlib.dll程序集System.Diagnostics名稱空間中ConditionalAttribute類的構造函數。可見,Attribute并不是修飾符,而是一個有著獨特實例化形式的類!

Attribute實例化有什么獨特之處呢?還是讓我們再次觀察中間語言代碼——它有兩個獨特之處。

1. 它的實例是使用.custom聲明的。查看中間語言語法,你會發現.custom是專門用來聲明自定義特性的。

2. 聲明的位置是在函數真正的代碼(IL_0000:至IL_0014)之前。

God,我懷疑是不是講的太深了。沒關系,上面關于中間語言的東西你都可以不care,只需要記住一個結論就可以了——我們已經從“底層”證明了Attribute不是什么“修飾符”,而是一種實例化方式比較特殊的類。

Attribute的實例化

就像牡蠣天生就要吸附在礁石或船底上一樣,Attribute的實例一構造出來就必需“粘”在一個什么目標上。

Attribute實例話的語法是相當怪異的,主要體現在以下三點上:

1. 不使用new操作符來產生實例,而是使用在方括號里調用構造函數的來產生實例。

2. 方括號必需緊挨著放置在被附著目標的前面。

3. 因為方括號里空間有限,不能像使用new那樣先構造對象后再對對象的屬性(Property)一一賦值。因此,對Attribute實例的屬性的賦值也都擠在了構造函數的圓括號里L

說實話,寫代碼的時候對于第1、第2兩條適應起來還算容易,第3條寫出來怎么看怎么別扭……而且尤其要記著的是:

1. 構造函數的參數是一定要寫的——有幾個就得寫幾個——因為你不寫的話實例就無法構造出來。

2. 構造函數參數的順序不能錯,這個很容易理解——調用任何一個函數你都不能改變參數的順序——除非它有相應的重載(Overload)。因為這個順序的固定的,所以有些書里管這些參數稱為“定位參數”,意即“個數和位置固定的參數”。

3. 對Attribute實例的屬性的賦值可有可無——反正它會有一個默認值。而且,先對哪個屬性賦值、后對哪個屬性賦值不受限制。有些書管這些為屬性賦值的參數叫“具名參數”——令人匪夷所思。

OK,百聞不如一見,還是讓我們自己寫一個Attribute類來體驗一下吧!

自己動手寫Attribute

這回我們拋棄.NET Framework給我們準備好的各種Attribute,從頭寫一個全新的Attribute——Oyster。

下面我給出一個完整的小例子:

//======水之真諦=======//
// 上善若水,潤物無聲 //
/* http://blog.csdn.net/FantasiaX */
using System;
namespace OysterAttributeSample
{
class Oyster: System.Attribute // 必需以System.Attribute類為基類
{
// Kind屬性,默認值為null
private string kind;
public string Kind
{
get { return kind; }
set { kind = value; }
}
// Age屬性,默認值為
private uint age;
public uint Age
{
get { return age; }
set { age = value; }
}
// 值為null的string是危險的,所以必需在構造函數中賦值
public Oyster(string arg) // 定位參數
{
this.Kind = arg;
}
}
[Oyster("Thorny ", Age=3)] // 3年的多刺牡蠣附著在輪船(這是一個類)上。注意:對屬性的賦值是在圓括號里完成的!
class Ship
{
[Oyster("Saddle")] // 0年的鞍形牡蠣附著在船舵(這是一個數據成員)上,Age使用的是默認值,構造函數的參數必需完整
public string Rudder;
}
class Program
{
static void Main(string[] args)
{
// ... 使用反射來讀取Attribute
}
}
}

為了不把代碼拖的太長,上面這個例子中Oyster類的構造函數只有一個參數,所以對“定位參數”體現的還不夠淋漓盡致。大家可以再為Oyster類添加幾個屬性,并在構造函數里多設置幾個參數,體驗一下Attribute實例化時對參數個數及參數位置的敏感性。

能被Attribute所附著的目標

讓我們思考這樣一個問題:牡蠣可以附著在船底、礁石上、橋墩上……甚至是別的牡蠣身上,那么Attribute呢?都可以將自己的實例附著在什么目標上呢?

這個問題的答案隱藏在AttributeTargets這個枚舉類型里——這個類型的可取值集合為:

=============================================================================

All Assembly Class Constructor

Delegate Enum Event Field

GenericParameter Interface Method Module

Parameter Property ReturnValue Struct

=============================================================================

一共是16個可取值。

不過,上面這張表是按字母順序排列的,并不代表它們真實值的排列順序。使用下面這個小程序可以查看每個枚舉值對應的整數值。

// =<水之真諦>=
// http://blog.csdn.net/FantasiaX
using System;
namespace AttributeTargetValue
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Assembly/t/t/t{0}", Convert.ToInt32(AttributeTargets.Assembly));
Console.WriteLine("Module/t/t/t/t{0}", Convert.ToInt32(AttributeTargets.Module));
Console.WriteLine("Class/t/t/t/t{0}", Convert.ToInt32(AttributeTargets.Class));
Console.WriteLine("Struct/t/t/t/t{0}", Convert.ToInt32(AttributeTargets.Struct));
Console.WriteLine("Enum/t/t/t/t{0}", Convert.ToInt32(AttributeTargets.Enum));
Console.WriteLine("Constructor/t/t/t{0}", Convert.ToInt32(AttributeTargets.Constructor));
Console.WriteLine("Method/t/t/t/t{0}", Convert.ToInt32(AttributeTargets.Method));
Console.WriteLine("Property/t/t/t{0}", Convert.ToInt32(AttributeTargets.Property));
Console.WriteLine("Field/t/t/t/t{0}", Convert.ToInt32(AttributeTargets.Field));
Console.WriteLine("Event/t/t/t/t{0}", Convert.ToInt32(AttributeTargets.Event));
Console.WriteLine("Interface/t/t/t{0}", Convert.ToInt32(AttributeTargets.Interface));
Console.WriteLine("Parameter/t/t/t{0}", Convert.ToInt32(AttributeTargets.Parameter));
Console.WriteLine("Delegate/t/t/t{0}", Convert.ToInt32(AttributeTargets.Delegate));
Console.WriteLine("ReturnValue/t/t/t{0}", Convert.ToInt32(AttributeTargets.ReturnValue));
Console.WriteLine("GenericParameter/t/t{0}", Convert.ToInt32(AttributeTargets.GenericParameter));
Console.WriteLine("All/t/t/t/t{0}", Convert.ToInt32(AttributeTargets.All));
Console.WriteLine("/n");
}
}
}

程序的運行結果是:

可能出乎你的預料——它們的值并不是步長值為1的線性遞增。你觀察出什么規律來了嗎?提醒你一下:從二進制的角度來考慮喔!!

我想你一定發現了規律——除了All的值之外,每個值的二進制形式中只有一位是“1”,其余位全是“0”。這就是枚舉值的另一種用法——標識位。那么,標識位有什么好處呢?

考慮這樣一種情況:我們的Attribute要求既能附著在類上,又能附著在類的方法上,應該怎么做呢?

我們知道,C#中有一個操作符“|”(也就是按位求“或”)。有了它,我們只需要書寫

AttributeTargets.Class | AttributeTargets.Method

就可以了。因為這兩個枚舉值的標識位(也就是那個唯一的“1”)是錯開的,所以只需要按位求或就解決問題了。我想,聰明的你一定立刻就能解釋為什么AttributeTargets.All的值是32767了吧:p

OK,了解了這些之后,我們應該怎樣控制一個Attribute的附著目標呢?

默認情況下,當我們聲明并定義一個新Attribute類時,它的可附著目標是AttributeTargets.All。大多數情況下AttributeTargets.All就已經滿足需求了,不過,如果你非要對它有所限制,那就要費點兒周折了。

還拿我們上面創建的OysterAttribute舉例——如果你想把它的附著目標限制為只有“類”和“值域”,你就應該這樣書寫:

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Field)]
class Oyster : System.Attribute
{
// OysterAttribute類的具體實現
}

沒想到吧!原來是用Attribute(的實例)附著在Attribute(類)上!本來嗎,Attribute的本質就是類,而AttributeTargets.Class 又說明Attribute可以附著在類上,所以,使用Attribute來“修飾”Attribute也就順理成章了J

最后,細心的讀者可能會問這樣兩個問題:

1. 如果一個Attribute附著在了某個類上,那么這個Attribute會為會隨著繼承關系也附著在派生類上呢?

2. 可不可以像多個牡蠣附著在同一艘船上那樣,讓一個Attribute的多個實例附著在同一個目標上呢?

Very good! 這真是兩個好問題!請看下面的代碼:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
class Oyster : System.Attribute
{
// OysterAttribute類的具體實現
}

原來,AttributeUsage這個用來專門修飾Attribute的Attribute除了可以控制修飾目標外,還能決定被它修飾的Attribute是否能夠隨宿主“遺傳”以及是否可以使用多個實例來修飾同一個目標!

OK,大家猜一猜,修飾ConditionalAttribute的AttributeUsage會是什么樣子呢?(提示:答案在MSDN里。)

TO BE CONTINUE

敬請關注:
《深入淺出Attribute(下)——Attribute V.S. Property》

法律聲明:本文章受到知識產權法保護,任何單位或個人若需要轉載此文,必需保證文章的完整性(未經作者許可的任何刪節或改動將視為侵權行為)。若您需要轉載,請務必注明文章出處為CSDN以保障網站的權益;請務必注明文章作者為劉鐵猛(http://blog.csdn.net/FantasiaX),并向liutm@beyondsoft.com發送郵件,標明文章位置及用途。轉載時請將此法律聲明一并轉載,謝謝!

轉載于:https://my.oschina.net/wzzz/blog/206186

總結

以上是生活随笔為你收集整理的深入浅出Attribute (转载)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。