C# Note21: 扩展方法(Extension Method)及其应用
前言
今天在开会时提到的一个概念,入职3个多月多注重在项目中使用C#的编程知识,一直没有很认真地过一遍C#的全部语法,当我们新人被问及是否了解Extension Method时,一时之间竟不能很通俗准确地描述。所以下面做个笔记吧。在空闲的时候还是要多看看MSDN的文档以及C#高级编程,熟悉相关语法知识,做到知其然也知其所以然!
(1)扩展方法有什么用?
扩展方法使您能够向现有类型添加方法,而无需创建新的派生类型、重新编译或修改原始类型。扩展方法是一种特殊的静态方法,但它们被称为扩展类型的实例方法。对于用c#和Visual Basic编写的客户机代码,调用扩展方法和在类型中定义的方法之间没有明显的区别。
C# 3.0就引入了该新特性,扩展方法可以很大的增加你代码的优美度,扩展方法提供了扩展.NET Framewoke类的扩展途径。
编写扩展方法有以下要求:
- 扩展方法所在的类必须是全局的,不能是内部嵌套类。
- 扩展方法的类是静态类。
- 扩展方法是静态方法。
- 扩展方法的第一个参数的数据类型必须是要扩展类型。
- 扩展方法的第一个参数使用this关键字。
(2)如何应用扩展方法?
有的时候,由于某些原因我们不能修改类本身的代码,但又需要在该类中增加功能,此时可以使用扩展方法。而且对于不能使用继承的结构和密封类来说,只能使用扩展方法增加功能。
注:一般而言,在非必要时,通过从现有类型中派生新类型来进行扩展,即继承的方式。因为,当使用扩展方法来扩展无法更改源代码的类型时,可能存在这样的风险:在实现类型中发生更改会导致扩展方法中断!
- 在编译时绑定扩展方法
You can use extension methods to extend a class or interface, but not to override them. An extension method with the same name and signature as an interface or class method will never be called. At compile time, extension methods always have lower priority than instance methods defined in the type itself. In other words, if a type has a method named Process(int i), and you have an extension method with the same signature, the compiler will always bind to the instance method. When the compiler encounters a method invocation, it first looks for a match in the type's instance methods. If no match is found, it will search for any extension methods that are defined for the type, and bind to the first extension method that it finds. --MSDN
上述说明了c#编译器在决定是否将方法调用绑定到类型上的实例方法或扩展方法时所遵循的规则:
-如果扩展方法具有与类型中定义的方法相同的签名,则永远不会调用它。(注:对相同名称不同参数类型的方法使用扩展方法是可以的。)
-当编译器无法找到带有匹配签名的实例方法时,它将绑定到匹配的扩展方法,如果存在的话。
示例:
// Define an interface named IMyInterface. namespace DefineIMyInterface { using System; public interface IMyInterface { // Any class that implements IMyInterface must define a method // that matches the following signature. void MethodB(); } } // Define extension methods for IMyInterface. namespace Extensions { using System; using DefineIMyInterface; // The following extension methods can be accessed by instances of any // class that implements IMyInterface. public static class Extension { public static void MethodA(this IMyInterface myInterface, int i) { Console.WriteLine ("Extension.MethodA(this IMyInterface myInterface, int i)"); } public static void MethodA(this IMyInterface myInterface, string s) { Console.WriteLine ("Extension.MethodA(this IMyInterface myInterface, string s)"); } // This method is never called in ExtensionMethodsDemo1, because each // of the three classes A, B, and C implements a method named MethodB // that has a matching signature. public static void MethodB(this IMyInterface myInterface) { Console.WriteLine ("Extension.MethodB(this IMyInterface myInterface)"); } } } // Define three classes that implement IMyInterface, and then use them to test // the extension methods. namespace ExtensionMethodsDemo1 { using System; using Extensions; using DefineIMyInterface; class A : IMyInterface { public void MethodB() { Console.WriteLine("A.MethodB()"); } } class B : IMyInterface { public void MethodB() { Console.WriteLine("B.MethodB()"); } public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); } } class C : IMyInterface { public void MethodB() { Console.WriteLine("C.MethodB()"); } public void MethodA(object obj) { Console.WriteLine("C.MethodA(object obj)"); } } class ExtMethodDemo { static void Main(string[] args) { // Declare an instance of class A, class B, and class C. A a = new A(); B b = new B(); C c = new C(); // 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. a.MethodA(1); // Extension.MethodA(object, int) a.MethodA("hello"); // Extension.MethodA(object, string) // A has a method that matches the signature of the following call // to MethodB. a.MethodB(); // A.MethodB() // B has methods that match the signatures of the following // method calls. b.MethodA(1); // B.MethodA(int) b.MethodB(); // B.MethodB() // B has no matching method for the following call, but // class Extension does. b.MethodA("hello"); // Extension.MethodA(object, string) // C contains an instance method that matches each of the following // method calls. c.MethodA(1); // C.MethodA(object) c.MethodA("hello"); // C.MethodA(object) c.MethodB(); // C.MethodB() } } } /* Output: Extension.MethodA(this IMyInterface myInterface, int i) Extension.MethodA(this IMyInterface myInterface, string s) A.MethodB() B.MethodA(int i) B.MethodB() Extension.MethodA(this IMyInterface myInterface, string s) C.MethodA(object obj) C.MethodA(object obj) C.MethodB() */
要启用特定类型的扩展方法,只需为定义方法的命名空间添加一个using指令。
另外,注意不能对原类型中private方法使用扩展方法,否则就破坏了设计的初衷!
朱颜辞镜花辞树,敏捷开发靠得住!