C#3.0擴展方法學習篇

 什麼是類的擴展方法

擴展方法使您能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。

MSDN

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type.

傳統的模式下如果想爲一個類型(class)添加一個額外的自定義的特殊的邏輯上,或者業務上的新方法時,你必須重新定義的一個類型來繼承原有的的方法,用繼承類或者接口,但是有些用sealed修飾的,這時候就無法被繼承,例如String,值類型,sealed修飾的類。

擴展方法是C#3.0這個版本提出來的。解決了必須由繼承才能擴展的某個類的弊端,最重要的一點就是很好用。

平時在我們使用的過程中也經常的見到,如
這其中的OrderBy方法就是擴展方法。
MSDN

注意:

擴展方法必須在非嵌套、非泛型的靜態類中定義。

Note that it is defined inside a non-nested<非嵌套>, non-generic<非泛型的> static<靜態> class:

擴展方法的規則有以下幾點:

  • 擴展方法必須是擴展方法必須是非嵌套、非泛型的靜態類中定義的;
  • 擴展方法的第一個參數要用this關鍵字修飾;
  • 第一個方法參數不能有ref 或則out關鍵字修飾的參數;
擴展方法的調用有兩個步驟
  1. 引用項目的命名空間;
  2. 參數調用兩種;
  • 和傳統的調用方法一樣使用<ExtendClass>.<ExtendClassMethod>(參數,參數+?)
  • <參數類型>.<ExtendClassMethod>(參數+?)
高級點的應用
    在編譯時綁定擴展方法<MSDN>
    你可以使用擴展方法來擴展一個類和接口,而不需要去重寫他們
  1 // Define an interface named IMyInterface.
  2 namespace DefineIMyInterface
  3 {
  4     using System;
  5 
  6     publicinterface IMyInterface
  7     {
  8         // Any class that implements IMyInterface must define a method
  9         // that matches the following signature.
 10         void MethodB();
 11     }
 12 }
 13 
 14 
 15 // Define extension methods for IMyInterface.
 16 namespace Extensions
 17 {
 18     using System;
 19     using DefineIMyInterface;
 20 
 21     // The following extension methods can be accessed by instances of any 
 22     // class that implements IMyInterface.
 23     publicstaticclass Extension
 24     {
 25         publicstaticvoid MethodA(this IMyInterface myInterface, int i)
 26         {
 27             Console.WriteLine
 28                 ("Extension.MethodA(this IMyInterface myInterface, int i)");
 29         }
 30 
 31         publicstaticvoid MethodA(this IMyInterface myInterface, string s)
 32         {
 33             Console.WriteLine
 34                 ("Extension.MethodA(this IMyInterface myInterface, string s)");
 35         }
 36 
 37         // This method is never called in ExtensionMethodsDemo1, because each 
 38         // of the three classes A, B, and C implements a method named MethodB
 39         // that has a matching signature.
 40         publicstaticvoid MethodB(this IMyInterface myInterface)
 41         {
 42             Console.WriteLine
 43                 ("Extension.MethodB(this IMyInterface myInterface)");
 44         }
 45     }
 46 }
 47 
 48 
 49 // Define three classes that implement IMyInterface, and then use them to test
 50 // the extension methods.
 51 namespace ExtensionMethodsDemo1
 52 {
 53     using System;
 54     using Extensions;
 55     using DefineIMyInterface;
 56 
 57     class A : IMyInterface
 58     {
 59         publicvoid MethodB() { Console.WriteLine("A.MethodB()"); }
 60     }
 61 
 62     class B : IMyInterface
 63     {
 64         publicvoid MethodB() { Console.WriteLine("B.MethodB()"); }
 65         publicvoid MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
 66     }
 67 
 68     class C : IMyInterface
 69     {
 70         publicvoid MethodB() { Console.WriteLine("C.MethodB()"); }
 71         publicvoid MethodA(object obj)
 72         {
 73             Console.WriteLine("C.MethodA(object obj)");
 74         }
 75     }
 76 
 77     class ExtMethodDemo
 78     {
 79         staticvoid Main(string[] args)
 80         {
 81             // Declare an instance of class A, class B, and class C.
 82             A a = new A();
 83             B b = new B();
 84             C c = new C();
 85 
 86             // For a, b, and c, call the following methods://      -- MethodA with an int argument//      -- MethodA with a string argument//      -- MethodB with no argument.// A contains no MethodA, so each call to MethodA resolves to // the extension method that has a matching signature.
 87             a.MethodA(1);           // Extension.MethodA(object, int)
 88             a.MethodA("hello");     // Extension.MethodA(object, string)// A has a method that matches the signature of the following call// to MethodB.
 89             a.MethodB();            // A.MethodB()// B has methods that match the signatures of the following// method calls.
 90             b.MethodA(1);           // B.MethodA(int)
 91             b.MethodB();            // B.MethodB()// B has no matching method for the following call, but // class Extension does.
 92             b.MethodA("hello");     // Extension.MethodA(object, string)// C contains an instance method that matches each of the following// method calls.
 93             c.MethodA(1);           // C.MethodA(object)
 94             c.MethodA("hello");     // C.MethodA(object)
 95             c.MethodB();            // C.MethodB()
 96         }
 97     }
 98 }
 99 /* Output:
100     Extension.MethodA(this IMyInterface myInterface, int i)
101     Extension.MethodA(this IMyInterface myInterface, string s)
102     A.MethodB()
103     B.MethodA(int i)
104     B.MethodB()
105     Extension.MethodA(this IMyInterface myInterface, string s)
106     C.MethodA(object obj)
107     C.MethodA(object obj)
108     C.MethodB()
109  */

 

編譯器是如何發現擴展方法的
c# 3.0編譯器看到某個類的調用用法時,先是從該類的實例方法中進行查找,如果沒有找到與調用方法同名並參數一致的實例方法,再從所有導入的命名空間中查找是否有合適的擴展方法,並將變量類型匹配到擴展類型。
這只是我們的理解是這樣一個過程,如果在同一個命名空間下,編譯器則會直接用當前命名空間下符合條件的對應方法,讓我看下下ILDasm.exe.
如果不是同一個程序集的命名空間可以在MANIFEST清單文件裏看出。

總結的結果

方法的調用次序 類型的實例方法--->當前命名空間下的擴展方法--->導入的其他命名空間擴展方法。

引發疑問

在空引用上調用實例方法或靜態方法時會拋出NullReferenceException異常?那麼在類調用擴展方法時會出現異常嗎?(不使用類中的一些屬性或方法,強調調用)

        public static void NullUse(this TestExtend sextend)
        {
            Console.WriteLine("NUll used Method");
        }
分析:在空的類型上定義擴展方法不出現NullReferenceException,只是把空這個引用當成參數傳入靜態方法,
擴展方法 靜態方法
TestExtend sExtend=null;
            sExtend.NullUse();
TestExtend sExtend=null;
NullUse.(sExtend);
 
 
 
從ILDASM能看出我們猜想的正確性
Notice

可能引發的子類污染的問題例如

        public static bool isNull(this object str)  
        {
            return str == null;
        }

你本來只想擴展TestExtend 這個類的方法,由於你要使用iSNull這個類型的擴展結果傳入參數(object),結果導致所有的object 類型都擴展了這個方法,這會帶來可怕的後果。

所以在擴展一個類型的方法時,要從確定類型擴展。儘量避免從父類去擴展。

有什麼不對的或者錯誤的地方希望大家給予指正,謝謝

我的開發環境VS2015

DEMO的下載  【http://pan.baidu.com/s/1bY51P8】

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章