扩展方法
扩展方法使您能够将方法“添加”到现有类型,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是静态方法,但它们被称为扩展类型的实例方法。
最常见的扩展方法是向现有 System.Collections.IEnumerable 和 System.Collections.Generic.IEnumerable<T> 类型添加查询功能的 LINQ 标准查询运算符。 要使用标准查询运算符,首先使用 using System.Linq 指令将它们纳入范围。
OrderBy 示例
下面的示例演示如何对整数数组调用标准查询运算符 OrderBy 方法。 括号中的表达式是一个 lambda 表达式。 许多标准查询运算符将 lambda 表达式作为参数,但这不是扩展方法的要求。
class ExtensionMethods2
{
static void Main()
{
int[] ints = { 10, 45, 15, 39, 21, 26 };
var result = ints.OrderBy(g => g);
foreach (var i in result)
{
System.Console.Write(i + " ");
}
}
}
//Output: 10 15 21 26 39 45
扩展方法定义为静态方法,但使用实例方法语法调用。 它们的第一个参数指定方法操作的类型。 参数前面有 this 修饰符。 仅当您使用 using 指令将命名空间显式导入源代码时,扩展方法才在范围内。
以下示例显示了为 System.String 类定义的扩展方法。
namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
WordCount 扩展方法可以通过这个 using 指令进入范围:
using ExtensionMethods;
并且可以使用以下语法从应用程序中调用它:
string s = "Hello Extension Methods";
int i = s.WordCount();
您可以使用实例方法语法在代码中调用扩展方法 , 编译器生成的中间语言 (IL) 将您的代码转换为对静态方法的调用。 封装的原则并没有真正被违反 , 扩展方法不能访问它们扩展类型的私有变量。
MyExtensions 类和 WordCount 方法都是静态的,可以像所有其他静态成员一样访问它。 WordCount 方法可以像其他静态方法一样被调用,如下所示:
string s = "Hello Extension Methods";
int i = MyExtensions.WordCount(s);
在编译时绑定扩展方法
您可以使用扩展方法来扩展类或接口,但不能重写它们。 永远不会调用与接口或类方法具有相同签名的扩展方法。 在编译时,扩展方法的优先级总是低于类型本身定义的实例方法。 换句话说,如果一个类型有一个名为 Process(int i) 的方法,并且你有一个具有相同签名的扩展方法,编译器将始终绑定到实例方法。 当编译器遇到方法调用时,它首先在类型的实例方法中查找匹配项。 如果未找到匹配项,它将搜索为该类型定义的任何扩展方法,并绑定到它找到的第一个扩展方法。 下面的示例演示了编译器如何确定要绑定到哪个扩展方法或实例方法。
示例
下面的示例演示了 C# 编译器在确定是将方法调用绑定到类型上的实例方法还是扩展方法时遵循的规则。 静态类 Extensions 包含为实现 IMyInterface 的任何类型定义的扩展方法。 A、B 和 C 类都实现了该接口。
MethodB 扩展方法永远不会被调用,因为它的名称和签名与类已经实现的方法完全匹配。
当编译器找不到具有匹配签名的实例方法时,它将绑定到匹配的扩展方法(如果存在)。
// 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(IMyInterface, int)
a.MethodA("hello"); // Extension.MethodA(IMyInterface, 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(IMyInterface, 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()
*/