从C# 3.0说以人为本(三)—— 扩展方法(转)

时间:2021-07-01 20:17:14
从C# 3.0说以人为本(三)—— 扩展方法

www.diybl.com 时间:2009-03-09 作者:匿名 编辑:sky

转自http://www.diybl.com/course/4_webprogram/asp.net/netjs/20090309/160297.html

说完了LINQ的语言支持和语法灵活,下面就是一个非常有用的东东——扩展方法(Extension Method)。

还记得我们看了好几遍的LINQ吧,下面这个:

  1.     var numQuery =
  2.     from num in numbers
  3.     where (num % 2) == 0
  4.     select num;

第一次讨论的时候我们讲到它等同于:

  1. IEnumerable numQuery = numbers.Where((number) => number % 2 == 0);

如果用2.0很熟悉的朋友,会知道Where函数是3.0新增的,同时新增的还有Sum,Count等一系列方法,在MSDN 2008中,我们可以看到这些函数归属于IEnumerable泛型接口成员,同时,他们都属于一个新的方法类型称为扩展方法,新的小图标是一个原来的方法图标边上加入一个向下的小箭头。

我们暂且先不讨论这个,先看看Array,List类,它们都扩展了IEnumerable,同时就具有了这些新增的方法。但是,这些方法是全部通过扩展接口重载了吗?好象不像,因为这些方法都大同小异,如果真的是用每个类重载实现的,代码的重复量就未免太大了。而且,如果是通过扩展接口实现的,又如何能称为“扩展方法”呢?

我们再仔细看看MSDN,在每个IEnumerable泛型接口扩展方法的后面,都有一个解释:(由 Enumerable 定义。)。越来越奇怪了!一个接口的方法由另外一个类定义?!这是什么东西?怎么由一个类定义一个接口的方法?

其实,这就是3.0中新增的扩展函数,扩展函数是一个非常非常有用的东西,它可以通过一个自定义的类扩展其他类的方法。例如:

  1. class ClassNeedExtensionMethod { }
  2. static class ClassHasExtensionMethod
  3.     {
  4. public static void ExtensionMethod(this ClassNeedExtensionMethod cn)
  5.         {
  6.             Console.WriteLine("Extension Method Invoked!");
  7.         }
  8.     }
  9. class ExtensionMethodTester
  10.     {
  11. public void Test()
  12.         {
  13.             ClassNeedExtensionMethod cn = new ClassNeedExtensionMethod();
  14. // 调用扩展方法 方式一
  15.             cn.ExtensionMethod();
  16. // 调用扩展方法 方式二
  17.             ClassHasExtensionMethod.ExtensionMethod(cn);
  18.         }
  19.     }

ClassNeedExtensionMethod是一个空空如也的类,但是在ExtensionMethodTester中却可以调用ExtensionMethod方法!这个方法是在ClassHasExtensionMethod中定义的!

情况是不是和IEnumerable很像?如果把ClassHasExtensionMethod换成Enumerable,把ClassNeedExtensionMethod换成IEnumerable,是不是就和MSDN所描述的情况完全一致了?如果你高兴,甚至可以扩展Object类的方法,太强大了!

不过扩展函数必须是在一个静态类中,并且自身也必须是一个静态函数,调用方法有两种,在上面的代码中已经用红字标出了。

扩展函数的出现能解决什么情况?不需要通过类继承的方式来继承某些函数了!

首先我们都知道,一个类只能继承自一个类,但是可以扩展无数个(理论上,不过谁有真的会给一个类继承无数接口呢……)接口。如果你发现类A,类B已经继承自类P,但是又要给A B都再继承一个父类C(这种情况是非常常见的,在项目中再普遍不过了),2.0唯一的做法就是:追根溯源,让P继承自C。万一P又已经继承自M,怎么办?最恶劣的情况,P继承自Page!难道你要让Page类继承自你的C类吗?你和微软商量去,这便导致了父类继承的最经典的情况。

但是有了扩展函数,如果只是要继承方法,而不需要继承变量的话,我们只需要扩展一个接口IC,然后帮IC扩展函数,那A B就具有这个已经定义好的函数了,完全不再需要考虑父类继承问题。

最经典的应用场景:0/R Mapping框架 ActiveRecord,本来需要继承父类以具有Save等方法,但是这样等于限制了以后的类继承条件,现在我们可以改良,使用 IActiveRecord,然后帮IActiveRecord扩展Save方法,同样能造成相同的使用而不会影响下层代码的改动,而且不再继承自任何父类!

扩展函数,.NET 3.0第三个以人为本的典例。

事实上……小凡觉得微软可能也发现使用LINQ后集合类需要扩展很多方法,但是也出现了类继承问题,于是绞尽脑汁想出了这个扩展函数的解决方案。不管怎么说,这个解决方案很巧妙,的确从根本出发解决了很多问题。