扩展方法允许在现有类型上扩展新的方法而无须修改原始类型的定义。扩展方法是静态类的静态方法,而其中的第一个参数需要用 this 修饰符修饰,且第一个参数的类型就是需要扩展的类型:
IsCapitalized 扩展方法可以像string的实例方法那样进行调用。例如:
编译后,扩展方法调用就会转换成普通的静态方法调用了:
这个转换过程如下:
接口也一样可以扩展:
扩展方法和实例方法类似,可以用简单的方式进行链式调用。考虑如下两个函数:
以下程序中的 x 和 y 是相同的,两者的最终结果均为 "Sausages" 。只不过 x 的计算过程使用了扩展方法,而 y 则使用了静态方法。
只有当包含扩展方法的类存在于当前作用域时(一般通过导入其所在的命名空间)我们才能够访问扩展方法。例如,以下示例中的扩展方法 IsCapitalized :
如果要使用 IsCapitalized ,那么下面的应用程序必须导入 Utils 命名空间,否则会出现编译时错误:
任何匹配成功的实例方法的优先级总是高于扩展方法。在下面的例子中,即使参数 x 类型为 int 也会优先调用 Test 的 Foo 方法:
在这个例子中,只能通过普通的静态调用语法来调用扩展方法,即 Extensions.Foo(...) 。
如果两个扩展方法签名相同,则必须使用普通的静态方法调用形式才能对二者进行区分。当然,如果其中一个扩展方法具有更具体的参数,那么相应方法优先级更高。
例如,考虑以下两个类:
以下代码将调用 StringHelper 的 IsCapitalized 方法:
需要注意的是,类型和结构体都比接口更加具体。
当Microsoft向.NET运行时库中添加的扩展方法与现存的第三方库中的扩展方法冲突时,则可能会造成一些“有趣”的结果。作为第三方库的作者,我可能希望“撤回”扩展方法,但并不删除它,也不破坏现有消费端的二进制兼容性。
万幸的是,以上要求是可行的,只需从扩展方法定义中移除 this 关键字即可。这个操作将扩展方法“降级”为普通的静态方法。这种解决方案的优雅之处在于任何之前依赖第三方程序库编译的程序集均可以继续正常工作(就像从前一样,调用定义的方法)。这是因为扩展方法调用在编译时会转换为静态方法调用。
只有在重新编译代码的时候,消费端才会因为扩展方法的降级而受到影响。此时,先前调用定义的扩展方法的代码将绑定到Microsoft定义的扩展方法上(假设已经引入了相应的命名空间)。如果消费端仍然希望调用先前定义的方法,则需要按照调用静态方法的方式进行书写。