C#中的擴展方法及用途

GPS平臺、網站建設、軟件開發、系統運維,找森大網絡科技!
http://cnsendnet.taobao.com
來自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=476php

擴展方法使你可以向現有類型「添加」方法,而無需建立新的派生類型、從新編譯或以其餘方式修改原始類型。 擴展方法是一種特殊的靜態方法,但能夠像擴展類型上的實例方法同樣進行調用。 以上是msdn官網對擴展方法的描述,如今我經過一個情景例子來對此進行闡釋。假設一個控制檯程序class Program{}裏面的主函數以下:編程

static void Main(string[] args)
{
DateTime now = DateTime.Now;
string time = now.ToString("yyyy-MM-dd HH:mm:ss");
Console.WriteLine(time);
Console.ReadKey();
}網絡

假設需求變了,日期的顯示格式要變成"yyyy-MM-dd"這種格式,固然只須要初始化time時按下面寫法改寫便可:
string time = now.ToString("yyyy-MM-dd");
可是若是要改變日期格式的有不少個類呢?每一個都要改一次嗎?這樣一旦需求變來變去就忙死人了。傳統的解決方式是封裝一個幫助類,在裏面寫方法,而後供其餘類調用。
本例在當前項目模仿添加一個DateHelper類:public class DateHelper{},在類裏面定義方法:
public static string DateToString(DateTime dt)
{
return dt.ToString("yyyy-MM-dd HH:mm:ss");
}
因而原來的主函數改寫以下:運維

static void Main(string[] args)
{
DateTime now = DateTime.Now;
string time = DateHelper.DateToString(now);
Console.WriteLine(time);
Console.ReadKey();
}ide

此時若是變需求,只須要改寫DateHelp類裏的DateToString()方法就好了,無論有多少個類調用此方法,都會被影響。問題解決了,但是這樣要調用另外一個類的方法,仍是有點麻煩,有沒有什麼方法可以讓咱們像now.DateToString()同樣直接調用呢?固然DateTime是微軟寫好的,咱們改不了,沒法建立想要的實例方法,因而,便引出了擴展方法。
下面是擴展方法的要素:
1.此方法必須是一個靜態方法
2.此方法必須放在靜態類中
3.此方法的第一個參數必須以this開頭,而且指定此方法是擴展自哪一個類型
根據以上要素,咱們DateHelper類改爲靜態類:public static class DateHelper{} ,同時改寫DateToString()方法:
public static string DateToString(this DateTime dt)
{
return dt.ToString("yyyy-MM-dd HH:mm:ss");
}
此時回到主函數方法體,輸入"now."即可以看見自動提示有個DateToString()方法,因而代碼能夠這樣寫:函數

static void Main(string[] args)
{
DateTime now = DateTime.Now;
string time = now.DateToString();
Console.WriteLine(time);
Console.ReadKey();
}學習

顯而易見,這樣用起來會更加便捷,並且這樣讓咱們看起來確實就像是被擴展類型自己具備的實例方法同樣,可讀性很高。下面歸納一下擴展方法的特色:
1.擴展方法擴展自哪一個類型,就必須是此類型的變量來使用,其餘類型沒法使用,本例擴展自DateTime類型,就只能是被DateTime類型的變量.出來(now.DateToString())
2.擴展方法中的this後面的參數不屬於方法的參數,本例是無參數,this後面的DateTime dt是指明擴展方法擴展自何種類型
3.若是擴展方法和實例方法具備相同的簽名,則優先調用實例方法
4.擴展自父類上的方法,能夠被子類的對象直接使用
5.擴展自接口上的方法,能夠被實現類的對象直接使用
6.擴展方法最終仍是被編譯器編譯成:靜態類.靜態方法(),本例中now.DateToString()最終仍是會被編譯成DateHelper.DateToString(now),這是它的本質
實際上,咱們可能會遇到這樣的情景,如在接口擴展一個方法的時候,全部的本來已實現該接口的類都要實現新擴展的方法,這樣的改動是一個很麻煩的工做,可使用擴展方法「曲線救國」;而有時候咱們想爲某個類添加新方法卻不想改動這個類,那麼擴展方法這種「僞添加」方法的方式就體現出它的價值了。最多見的擴展方法是LINQ標準查詢運算符,運用普遍,這種方便快捷的方式理應博得碼農們點1024個贊。動畫

什麼是擴展方法?
  擴展方法從字面上理解是指擴展的方法,而對應到面向對象編程這個格局中則是指爲一個類提供的擴展方法。按照咱們一般的理解,咱們首先須要得到某個類的源代碼,而後在這個類代碼中增長成員方法,這樣就能夠達到爲一個類提供擴展方法的目的。但是不幸地是,這種方法在沒有源代碼的狀況下就沒法奏效了,並且咱們人爲地去改變源代碼有可能會破壞整個代碼的穩定性。那麼有沒有一種方法能在不改變源代碼的前提下爲某個類提供擴展方法呢?這就是咱們今天要說的擴展方法,因此咱們能夠將擴展方法理解爲在不改變源代碼的前提下向外部提供擴展方法的一種方式。C#中的擴展方法實現起來是相對來講比較簡單的,例如咱們作在Unity3D遊戲開發的時候,可能會用到DOTween這個插件。這個插件是iTween的做者從新編寫一個動畫插件,效率上比iTween有較大的提高。更爲重要的一點是,它採用擴展方法這種實現方式,使得咱們在調用這些API接口的時候難以感受到咱們是在使用一個插件,更像是在使用Unity3D的原生函數,因此當咱們使用DOTween + uGUI 這樣的組合的時候,心裏會感到無比的舒暢,一切都像是水到渠成通常。
擴展方法有哪些特色?
  擴展方法在實現上和普通的面向對象編程是同樣的,換句話說,咱們只須要定義一個類,而後在裏面添加並實現相應的方法便可。可是這裏須要注意的地方有三點,第一,實現擴展方法的類必須是靜態類且類的名稱和實現擴展方法的類無關;第2、實現擴展方法的類方法必須是靜態方法;第3、實現擴展方法的類方法的第一個參數必須是使用this關鍵字指明要實現擴展方法的類。例如,咱們知道將一個合法字符串類型轉換爲整型,可使用int.parse()方法,假如咱們但願爲string類型擴展一個ToInt方法應該怎麼辦呢?咱們一塊兒來看下面的這段代碼:網站

  1. /// <summary>
  2. /// 一、定義一個靜態類
  3. /// 二、靜態類的名稱和要實現擴展方法的具體類無關
  4. /// </summary>
  5. public static class SomeClass
  6. {
  7. /// <summary>
  8. /// 三、實現一個具體的靜態方法
  9. /// </summary>
  10. /// <param name="str">四、第一個參數必須使用this關鍵字指定要使用擴展方法的類型</param>
  11. /// <returns></returns>
  12. public static int ToInt(this string str)
  13. {
  14. return int.Parse(str);
  15. }
  16. }
    須要注意的是C#支持擴展方法是從.NET3.5版本開始,因此在編寫擴展方法的時候請確保你的.NET版本是否知足這一要求。提到版本問題,有不少朋友尤爲是從Unity5.0之後開始學習Unity3D的朋友,經常會在個人博客中留言提到個人代碼沒法在新環境下運行等等相似地問題,我以爲這個世界上更新速度最快的當屬IT技術了,你們使用新版本沒有問題,但是有時候由於技術發展中的歷史遺留問題例如Python2.7和Python三、Unity4.X和Unity5.X,這個時候可能出現版本不兼容的問題,這個時候若是網絡上的資源沒有及時更新,建議你們仍是及時查看官方的最新文檔,由於在博主看來網絡上的書籍或者相關文章都是用來參考的,古話說:盡信書不如無書,只有客觀、冷靜地判斷知識的正確與否,咱們方能學到真正有用的知識。
      好了,如今咱們編寫完這個擴展方法之後,就能夠像下面這樣使用擴展方法了:
  17. string str = "1234";
  18. int val = str.ToInt();
    這個示例向你們展現瞭如何編寫一個無參數的擴展方法,那麼當咱們須要在擴展方法中傳入參數的時候該怎麼作呢?咱們只須要在第一個參數後繼續加入參數的聲明就行了。例如咱們在Unity3D中經常須要給一個3D物體設置座標,一般咱們能夠經過下面的代碼來實現:
    transform.position = new Vector3(1,1,1);
    這個代碼到目前爲止是比較簡潔的,但是咱們知道在Unity3D中除了position屬性之外還有localPosition屬性,若是咱們的代碼中再涉及座標計算的話,我相信這個代碼必定會變得很是的長。更有甚者,有時候咱們只想改變三維座標中的一個維度,但是咱們必須給transform.position一個三維座標,毫無心外地此時的代碼會變得更長。爲了解決這個問題,咱們能夠擴展出三個方法SetPositionX、SetPositionY、SetPositionZ來分別爲x、y、z三個座標份量進行賦值,咱們繼續在SomeClass這個類中添加方法:
  19. /// <summary>
  20. /// 設置Tranform的X座標
  21. /// </summary>
  22. /// <param name="tran">當前Transform</param>
  23. /// <param name="x">X座標</param>
  24. public static void SetPositionX(this Transform tran, float x)
  25. {
  26. tran.position = new Vector3(x, tran.position.y, tran.position.z);
  27. }
  28. /// <summary>
  29. /// 設置Tranform的Y座標
  30. /// </summary>
  31. /// <param name="tran">當前Transform</param>
  32. /// <param name="x">Y座標</param>
  33. public static void SetPositionY(this Transform tran, float y)
  34. {
  35. tran.position = new Vector3(tran.position.x, y, tran.position.z);
  36. }
  37. /// <summary>
  38. /// 設置Tranform的Z座標
  39. /// </summary>
  40. /// <param name="tran">當前Transform</param>
  41. /// <param name="x">Z座標</param>
  42. public static void SetPositionZ(this Transform tran, float z)
  43. {
  44. tran.position = new Vector3(tran.position.x, tran.position.y, z);
  45. }
    使用擴展方法的利弊
      擴展方法使用起來駕輕就熟,因此咱們這裏來討論下使用擴展方法的利弊。好處固然是自由而任性地使用擴展方法對類進行擴展,並且擴展方法在Visual Studio中的智能提示會以藍色向下箭頭進行標識。擴展方法的壞處則是要看設計擴展方法的人可否較好的駕馭這個特性啦,其實全部的技術都是同樣的,我經常在遊戲羣裏聽到人鄙視Unity3D引擎,以UnReal Engine4爲遊戲引擎世界裏的泰山北斗,我認可UE4的畫面效果好,但是能真正用好這個引擎的人有多少呢?擴展方法在使用的時候應該遵照就近原則,便是在最小的範圍內使用擴展方法,對具體類而非抽象類實現擴展方法。咱們使用擴展方法無非是由於它在邏輯層須要這樣的功能,因此咱們沒有必要去改變抽象層的邏輯,由於這樣會「污染」整個代碼。舉一個簡單的例子,咱們知道.NET中的基類是object,若是咱們對這個類進行擴展,毫無疑問它會影響全部繼承自object的類,這樣就會形成「污染」,顯然是不可取的。
    小結
    • 在C#中實現擴展方法的類必須是靜態類且類的名稱和實現擴展方法的類無關
    • 實現擴展方法的類方法必須是靜態方法
    • 實現擴展方法的類方法的第一個參數必須是使用this關鍵字指明要實現擴展方法的類
    • 實現擴展方法應遵照就近原則,在最小的範圍內使用擴展方法以免形成「污染」

GPS平臺、網站建設、軟件開發、系統運維,找森大網絡科技!
http://cnsendnet.taobao.com
來自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=476this

相關文章
相關標籤/搜索