標籤:
什麼是類的擴充方法
擴充方法使您能夠向現有類型“添加”方法,而無需建立新的衍生類別型、重新編譯或以其他方式修改原始類型。
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關鍵字修飾的參數;
擴充方法的調用有兩個步驟
- 引用項目的命名空間;
- 參數調用兩種;
- 和傳統的調用方法一樣使用<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資訊清單檔裡看出。
總結的結果
方法的調用次序 類型的執行個體方法--->當前命名空間下的擴充方法--->匯入的其他命名空間擴充方法。
引發疑問
在Null 參考上調用執行個體方法或靜態方法時會拋出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】
C#3.0擴充方法學習篇