注意:本次小序頗長而且沒什麼正事兒,建議大家直接跳到正文,以免浪費寶貴時間:)
積水潭橋旁的物美大賣場有兩層——B1和B2。B1賣電器和日用品(概括起來說就是那些不能吃的——貓糧除外),B2是賣食品的,又分為兩個區,南區是賣零食的,北區是賣蔬菜水果生肉熟肉的。以前轉的最多的是南區,購物車裡大包小包一堆零食還要再抱上兩大桶可樂,衣着整潔的時候偶爾還假裝斯文給打扮入時的小MM讓個路、擠個眉弄個眼兒什麼的;現在基本上隻在北區轉了,購物車裡全是折價蔬菜大米油鹽,每天都是褲衩背心、趿拉着拖鞋,經常是跟退休的大媽們混戰在一起扒拉架豆角或者擗菜幫……
喔~~~逛超市最麻煩的倒不是買東西,而是排大長隊付款——尤其是周末:p
不過,本周末晚上我可是碰見一猛人,PLMM。她先是從北區後場啟動,嘁哩喀喳裝了一堆菜到稱重處過秤,然後靈活地繞過幹貨區,然後在到達南北區的中線的時候來了個急停。左顧右盼幾秒鐘後,“啪、啪”裝了兩大包FB面之後便突然啟動殺入了零食林立的南區中場——隻見她矯健的身影在零食區的貨架間穿插自如、大搞S形機動,桑巴之态如入無人之境,幾乎overload的購物車在她腳下,不,是手中!左盤右帶、就像魔術師手中的道具一樣靈活。她打開一張紙,大概是要買的東西,從上到下掃描一遍後自信地點了下頭——看來是要買的東西已經買齊了,然後眯起眼睛、遠遠地望了一眼收銀台,臉上略過一絲不易察覺的、邪邪的笑……
看!她加速了,加速了!!她已經沖過了飲料區,兩個顧客橫在過道出口、封住了她前進的道路。隻見她又是一個急停,把車以90度角向左一推,然後再以90度角向右一轉——這是多麼流暢的普加喬夫眼鏡蛇機動!兩個顧客還沒有反應過來,她已經殺出了飲料區,直奔收銀台……
離收銀台不足十碼了!眼看就有兩個顧客要卡在她前面交費了!怎麼辦!怎麼辦?!
她先是利用自己的速度優勢殺到較近的一個顧客身旁,輕挑車頭,打算用假動作騙過這名顧客,讓這名顧客迫于壓力進入另一付費通道。看!她成功了!!而這時,另一名顧客馬上就要進入交費通道了——刹那間,她做出了另人不可思議的動作!她先是把車往左一帶,然後又往右一撥,雙手突然發力腳下同時加速——人車分過!人車分過!!!哇噻!!她又成功了!!此時,她面前隻有收銀員了!!面對收銀員她絲毫沒有遲疑、沒有猶豫、沒有任何放慢速度的意思——偉大的意大利左後衛!!!勝利是屬于你的!!!
嘟~~~~~~~~~~~~~~
在警報響起的同時,收銀員穩穩地将車抱入懷中!
沖撞收銀員!超市管理者跑過來了,一邊跑一邊~~~好像是在掏牌兒~~~讓我們看看他掏的是什麼牌兒……哇哦!紅牌,是紅牌!!
兩名保安走過來,準備把PLMM架出場外。超市管理者來到收銀員旁邊,察看收銀員有沒有受傷。
怎麼回事?!場上一陣騷亂!一名保安倒在了地上,痛苦地捂着胸口在地上打滾!這到底是怎麼回事?讓我們看一下超市監控錄像的慢動作回放……保安先是拉了一下MM的T Shirt,被MM用手撥開,走了兩步之後,MM突然沖回來用頭狠狠地頂在保安的胸口!保安淩空翻轉,重重摔在地上!——God!這到底是怎麼回事?!保安到底對MM做了什麼,或者是說了什麼,以緻MM出此狠招?!
……
什麼是Attribute?Attribute是幹什麼使的?Attribute與Property到底有什麼差別?……
長久以來,這些問題一直困擾着并不怎麼廣大的C#初學者。原因大概有兩個,一是Attribute平時不怎麼常用(沒用慣怎麼可能常用嗎!)二是這個家夥不太好翻譯——它與Property這個詞意義相近,都有“屬性”這個詞條(而且在HTML語言中,Attribute也的确與C#面向對象概念中的Property意思一緻),是以很多譯者,特别是C#剛剛出來那陣子,拿捏不好應該怎麼翻譯,搞的C#初學者一遇到“屬性”就發懵、一遇到“Attribute”就發怵。現在情況還算比較明朗了,Attribute一般譯作“特性”,Property仍然譯為“屬性”。
今天,先讓我們來學習一下到底什麼是Attribute并體驗一下Attribute的威力,然後讓我們徹底澄清Attribute與Property之間的差別。
大多數書籍都喜歡講——“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登場!
請編譯運作下面這個程式:
//====水之真谛====//
//上善若水,潤物無聲//
//#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")] // 長記法
public static void Method2() { Console.WriteLine("Created By Li, NoBug."); }
[Conditional("LI")] // 短記法
[Conditional("NOBUG")] // 換序
[Conditional("LI")]
[Conditional("NOBUG"), Conditional("LI")] // 單括号疊加
當我們對Attribute的用途有所了解後,我們就可以向Attribute的本質進發了!
本文轉自 水之真谛 51CTO部落格,原文連結:http://blog.51cto.com/liutiemeng/29201,如需轉載請自行聯系原作者