随着.Net Framework一路走来,已经让广大开发人员体验到快速开发的甜头,这得益于.Net Framework为我们提供了更高层次的封装,开发人员不必关心底层的Win32 API及其繁琐的调用参数,而可以把大部分的经历放在对业务的分析和实现。随着.Net的不断革新,也引入了更多的特性,例如C# 2.0,就增加了匿名方法和迭代器,这些特性让我们的编码效率更高。随着C# 3.0的推出,引入了更多的新特性,包括:隐式类型局部变量、对象初始化器、Lambda表达式、扩展方法、匿名类型。
而这些特性,都为LINQ的推出,构建好了基础。其中一个比较不错的特性,就是扩展方法。扩展方法,顾名思义,就是在类型定义完成之后,再继续为其添加新的方法。这是相当方便的,比如对于一个已经封装好的Assembly来说,我们不用改动Assembly的代码,而通过扩展方法就能实现对其功能的扩展,而且在调用的时候,仅仅通过代码,你几乎判断不出这是扩展方法,还是Assembly本身的方法。
扩展方法在.Net 3.5中的应用也是非常普遍的,如果你仔细观察,就会发现我们常用的Linq to Object中的Where,Select,Average,Sum等方法,以及Linq to SQL中的Where,Select,Average,Sum等方法,其实都是扩展方法,他们分别定义于System.Linq.Enumerable类和System.Linq.Queryable类之中。
就扩展方法本身而言,也存在一些限制之处。对于编译器来说,如果扩展方法和被扩展类型的方法发生冲突的时候,在调用此方法的时候,究竟是调用被扩展类型的方法,还是扩展方法呢?通过一个例子,我们就能发现其中的区别。
<span class="lnum"> 1: </span> <span class="kwrd">class</span> Program
<span class="lnum"> 2: </span> {
<span class="lnum"> 3: </span> <span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)
<span class="lnum"> 4: </span> {
<span class="lnum"> 5: </span> <span class="kwrd">new</span> TestClassA().Display(<span class="str">"Test"</span>);
<span class="lnum"> 6: </span> <span class="kwrd">new</span> TestClassB().Display(<span class="str">"Test"</span>);
<span class="lnum"> 7: </span> Console.ReadLine();
<span class="lnum"> 8: </span> }
<span class="lnum"> 9: </span> }
<span class="lnum"> 10: </span>
<span class="lnum"> 11: </span> <span class="kwrd">class</span> TestClassA
<span class="lnum"> 12: </span> {
<span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Display(<span class="kwrd">int</span> b)
<span class="lnum"> 14: </span> {
<span class="lnum"> 15: </span> Console.WriteLine(<span class="str">"This is TestClassA.Display() ..."</span>);
<span class="lnum"> 16: </span> }
<span class="lnum"> 17: </span> }
<span class="lnum"> 18: </span>
<span class="lnum"> 19: </span> <span class="kwrd">class</span> TestClassB
<span class="lnum"> 20: </span> {
<span class="lnum"> 21: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Display(<span class="kwrd">string</span> s)
<span class="lnum"> 22: </span> {
<span class="lnum"> 23: </span> Console.WriteLine(<span class="str">"This is TestClassB.Display() ..."</span>);
<span class="lnum"> 24: </span> }
<span class="lnum"> 25: </span> }
<span class="lnum"> 26: </span>
<span class="lnum"> 27: </span> <span class="kwrd">static</span> <span class="kwrd">class</span> TestClassExtention
<span class="lnum"> 28: </span> {
<span class="lnum"> 29: </span> <span class="kwrd">static</span> <span class="kwrd">public</span> <span class="kwrd">void</span> Display(<span class="kwrd">this</span> <span class="kwrd">object</span> o, <span class="kwrd">string</span> s)
<span class="lnum"> 30: </span> {
<span class="lnum"> 31: </span> Console.WriteLine(<span class="str">"This is TestClassExtention.Display() ..."</span>);
<span class="lnum"> 32: </span> }
<span class="lnum"> 33: </span> }
程序输出:
可以看出,TestClassA的方法和扩展方法并没有冲突,因为他们的方法签名是不一样的,而TestClassB的方法和扩展方法有冲突,因为他们的都是接受一个string类型的输入参数。从结果可以看到,程序对Display的方法调用,TestClassB本身的Display方法,要“优先”于扩展方法Display被调用。因此,类本身的方法如果满足调用条件,那么这个方法会被优先执行,只有在类当中无法找到同样参数的方法时,扩展方法才有机会被执行。所以,我们可以得出这样的结论:扩展方法的优先级较低,也即扩展方法不会覆盖同名的类本身的方法。
另外,还有一个比较明显的区别,就是扩展方法要远远弱于类本身的方法,比如,在一个类中,类的方法可以访问自己的非公有成员,而扩展方法做不到这一点。