分类: C#/.net
2014-09-29 14:32:00
如同先前提到的,LINQ状态依赖于IEnumerable接口的实现对象,比如,下面的简单函数会合计帐户集合中的帐户余额:
public decimal SumAccounts(IEnumerable
在上面的代码中,myAccounts参数的类型被声明为IEnumerable
但是Sum方法应该定义到何处?C#是强类型的语言,时时彩论坛因此如果Sum方法的引用是无效的,C#编译器会对其报错。我们知道它必须存在,但是应该在哪里呢?此外,LINQ提供的供查询和聚集结果所有方法在哪里定义呢?
答案是Sum并不在IEnumerable接口内定义,而是一个定义在System.Linq.Enumerable类中的static方法(叫做“extension method”)
namespace System.Linq { public static class Enumerable { ... // the reference here to “this IEnumerable可是扩展方法和其它静态方法有什么不同之处,是什么确保我们可以在其它类访问它?
扩展方法的显著特点是第一个形参前的this修饰符。这就是编译器知道它是一个扩展方法的“奥妙”。它所修饰的参数的类型(这个例子中的IEnumerable
(另外需要指出的是,定义扩展方法的IEnumerable接口和Enumerable类的名字间的相似性没什么奇怪的。这种相似性只是随意的风格选择。)
理解了这一点,我们可以看到上面介绍的sumAccounts方法能以下面的方式实现:
public decimal SumAccounts(IEnumerable事实上我们可能已经这样实现了这个方法,而不是问什么要有扩展方法。扩展方法本身只是C#的一个方便你无需继承、重新编译或者修改原始代码就可以给已存的在类型“添加”方法的方式。
扩展方法通过在文件开头添加using [namespace];引入到作用域。你需要知道你要找的扩展方法所在的名字空间。如果你知道你要找的是什么,这点很容易。
当C#编译器碰到一个对象的实例调用了一个方法,并且它在这个对象的类中找不到那个方法,它就会尝试在作用域中所有的扩展方法里找一个匹配所要求的类和方法签名的。如果找到了,它就把实例的引用当做第一个参数传给那个扩展方法,然后如果有其它参数的话,再把它们依次传入扩展方法。(如果C#编译器没有在作用域中找到相应的扩展方法,它会抛措。)
对C#编译器来说,扩展方法是个“语法糖”,使我们能把代码写得更清晰,更易于维护(多数情况下)。显然,前提是你知道它的用法,否则,它会比较容易让人迷惑,尤其是一开始。
应用扩展方法确实有优势,但也会让那些对它不了解或者认识不正确的开发者头疼,浪费时间。尤其是在看在线示例代码,或者其它已经写好的代码的时候。当这些代码产生编译错误(因为它调用了那些显然没在被调用类型中定义的方法),一般的倾向是考虑代码是否应用于所引用类库的其它版本,甚至是不同的类库。很多时间会被花在找新版本,或者被认为“丢失”的类库上。
在扩展方法的名字和类中定义的方法的名字一样,只是在方法签名上有微小差异的时候,甚至那些熟悉扩展方法的开发者也偶尔犯上面的错误。很多时间会被花在寻找“不存在”的拼写错误上。
在C#中,用扩展方法变得越来越流行。除了LINQ,在另外两个出自微软现在被广泛使用的类库Unity Application Block和Web API framework中,也应用了扩展方法,而且还有很多其它的。框架越新,用扩展方法的可能性越大。
当然,你也可以写你自己的扩展方法。体球网但是必须意识到虽然扩展方法看上去和其它实例方法一样被调用,但这实际只是幻。事实上,扩展方法不能访问所扩展类的私有和保护成员,所以它不能被当做传统继承的替代品。