3.10 扩展方法
有许多扩展类的方式。继承就是给对象添加功能的好方法。扩展方法是给对象添加功能的另一个选项,在不能使用继承时,也可以使用这个选项(例如类是密封的)。
注意:扩展方法也可以用于扩展接口。这样,实现该接口的所有类就有了公共功能。
扩展方法是静态方法,它是类的一部分,但实际上没有放在类的源代码中。
假设希望用一个方法扩展string类型,该方法计算字符串中的单词数。GetWordCount方法利用String.Split方法把字符串分割到字符串数组中,使用Length属性计算数组中元素的个数 (代码文件ExtensionMethods / Program.cs):
public static class StringExtension
{
public static int GetWordCount(this string s) =>
s.Split().Length;
}
使用this关键字和第一个参数来扩展字符串。这个关键字定义了要扩展的类型。
即使扩展方法是静态的,也要使用标准的实例方法语法。注意,这里使用fox变量而没有使用类型名来调用GetWordCount ()。
string fox = "the quick brown fox jumped over the lazy dogs down " +
"9876543210 times";
int wordCount = fox.GetWordCount();
WriteLine($"{wordCount} words");
在后台,编译器把它改为调用静态方法:
int wordCount = StringExtension.GetWordCount(fox);
使用实例方法的语法,而不是从代码中直接调用静态方法,会得到一个好得多的语法。这个语法还有一个好处:该方法的实现可以用另一个类取代,而不需要更改代码——只需要运行新的编译器。
编译器如何找到某个类型的扩展方法?this关键字必须匹配类型的扩展方法,而且需要打开定义扩展方法的静态类所在的名称空间。如果把StringExtensions类放在名称空间Wrox. Extensions中,则只有用using指令打开Wrox.Extensions,编译器才能找到GetWordCount方法。如果类型还定义了同名的实例方法,扩展方法就永远不会使用。类中已有的任何实例方法都优先。当多个同名的扩展方法扩展相同的类型,打开所有这些类型的名称空间时,编译器会产生一个错误,指出调用是模棱两可的,它不能决定在多个实现代码中选择哪个。然而,如果调用代码在一个名称空间中,这个名称空间就优先。
注意:语言集成查询(Language Integrated Query, LINQ)利用了许多扩展方法。