淺析C#擴展方法
在我們的編程生涯中我們要使用很多很多類庫,這些類庫有的是我們自己開發(fā)的,我們有她的代碼,有的是第三方發(fā)布的,我們不僅沒有他們的代碼,連看的機會都沒有。
作為.net程序員,我們每天都要和BCL(Base Class Linbrary)打交道。無疑,BCL做為一個年輕的框架類庫,她是成功的,但是還有一些時候我們還是得寫一些”Helper”方法來擴展類庫,由于我們不能修改類庫的源代碼,我們只有寫一個個的靜態(tài)類。雖然在使用上也算方便,但作為追求***的程序員來說總有些不雅?,F(xiàn)在我就碰到這樣的事情,前兩天奉命寫一個從XML文件加載Chart圖的設置的方法,從XML加載數(shù)據(jù)綁定到對象上,這肯定是反射的用武之地了。我經(jīng)常需要寫一些根據(jù)對象屬性名字來判斷這個對象是否有這個屬性或者根據(jù)屬性名獲取該屬性的值。還是按照平常一樣,我很快寫了一個PropertyHelper,里面有兩個靜態(tài)方法:HasProperty,GetValueByName。
PropertyHelper.HasProperty(point, "X"),如此的調用也還過得去,不過在C# 3.0微軟為我們提供了C#擴展方法?,F(xiàn)在我們可以直接這樣調用了point.HasProperty(“X”);看看我是如何實現(xiàn)這個擴展方法的?
- publicstaticclassPropertyExtension
- {
- publicstaticobjectGetValueByName(thisobjectself,stringpropertyName)
- {
- if(self==null)
- {
- returnself;
- }
- Typet=self.GetType();
- PropertyInfop=t.GetProperty(propertyName);
- returnp.GetValue(self,null);
- }
- }
我給object類型添加了一個擴展方法,在.net里所有的類都繼承自object,那所有的類都默認的擁有這個方法了,真方便,呵呵。
注意到和普通的靜態(tài)方法有何差別?在這個方法的***個參數(shù)前面多了一個this關鍵字。
擴展方法:
1.方法所在的類必須是靜態(tài)的
2.方法也必須是靜態(tài)的
3.方法的***個參數(shù)必須是你要擴展的那個類型,比如你要給int擴展一個方法,那么***個參數(shù)就必須是int。
4.在***個參數(shù)前面還需要有一個this關鍵字。
按照上面的步驟寫你就得到了一個“C#擴展方法”,你可以像調用這個類的原生方法那樣去調用它:
- stringstr="abc";
- objectlen=str.GetValueByName("Length");
好像string類型現(xiàn)在有了GetValueByName這個方法一樣,但實際上string并沒有這樣一個方法。那這又是為什么呢?是我們可愛的編譯器在其中做了手腳。為了避開編譯器的干擾,我們來直接欣賞MSIL代碼:
- L_0008:ldstr"Length"
- L_000d:callobjectTestLambda.PropertyExtension::GetValueByName(object,string)
從MSIL中我們可以看出,這段代碼編譯后和調用靜態(tài)方法沒有任何的差別(從call指令來看,這是在調用一個靜態(tài)方法)。
從這里可以知道擴展方法即可以使用實例調用的方式也可以直接使用靜態(tài)類調用的方式:
- str.GetValueByName("Length");
- PropertyExtension.GetValueByName(str,"Length");
擴展方法有就近原則,也就是如果在你的程序里有兩個一模一樣的擴展方法,一個和你的使用類是處于同一命名空間里,另外一個處于別的命名空間里,這個時候會優(yōu)先使用同一命名空間里的擴展方法,也就是說“血緣關系”越近,越被青睞。
很多人看到擴展方法也許眼里冒出金光,以后在設計的時候什么都不管,反正可以擴展。還有一些人會對類任意擴展,將以前一些作為”Helper”的方法統(tǒng)統(tǒng)使用C#擴展方法代替,注意的是擴展方法有“污染性”,所以我覺得在擴展的時候還是想想,是不是值得這樣擴展。
在擴展的時候也不要對比較高層的類進行擴展,像我上面對object的擴展我覺得就是不可取的,object是所有類的基類,一經(jīng)擴展,所有的類都被“污染”了。
【編輯推薦】