在开发时,有时会发现已定的类的方法并不能够全部满足需求,这时如果要扩展这个类,可以考虑通过继承新增一个类,那如如原来的类是密封类(sealed),那不就无法继承呢?这该怎么办呢?有人会说,这时可以再新建一个类,并将类名在原类上加一个加缀Extension。比如原类是Employee,这时可以叫做EmployeeExtension。这当然是可以的。
那还有没有什么办法可以在沿用原类名的基础上去扩展该类的方法呢?答案是有的!这就是要用到类的扩展。我们先来扩展一个系统类,比如DirectoryInfo。在使用DirectoryInfo时,可能会希望增加一个CopyTo的方法以实现将目录下的文件拷贝到另一个目录下,但原DriectoryInfo是没有的,看下图。
新建了一个DirectoryInfo的实例directory,然后展开相应的方法,在Attributes后就是Create,直到CreationTimeUtc的所有C开头的方法和属性中并没有一个CopyTo的方法。现在我们再看看通过扩展后的图。
我们可以看到,这时的directory这时多了CopyTo的方法,而且前面的图标多了一个表示扩展的向下箭头,同时右边的说明部分也标有“扩展”的字样。那这个是如何添加进去的呢?我们先来看扩展的代码
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace Extensions { /// <summary> /// Extension DirectoryInfo. /// </summary> public static class DirectoryInfoExtension { /// <summary> /// Copy source directory to target directory. /// </summary> /// <param name="sourceDirectory"></param> /// <param name="target"></param> /// <param name="option"></param> /// <param name="searchParttern"></param> public static void CopyTo(this DirectoryInfo sourceDirectory, string target, SearchOption option, string searchParttern) { //TODO Console.WriteLine("Copy to {0}", target); } } }
首先,这里我们增加了一个静态类DirectoryInfoExtension(类名不限定,不过习惯上是在原类名上加Extension的后缀),注意一定要是静态方法。
其次,在这个静态类中添加了一个CopyTo的静态方法,注意一定要是静态方法。
再次,在CopyTo这个方法中,第一个参数是以this开始,同时以要扩展的类作为类型DirectoryInfo,并定义一个参数sourceDirectory(该参数在调用CopyTo方法时,并不作为传入参数)。
最后,在第一个参数之后定义target等几个参数是CopyTo方法被调用时实际使用的参数,然后方法体内的代码和普通的方法一样。
需要注意几点
1.这里虽然是以静态方法定义的,但是在使用时却是要以实例的形式来调用,即定义DirectoryInfo的实例directory,然后以directory.CopyTo调用。
2.在使用的地方,一定要记得添加扩展的命名字间引用,如上例中是在Extensions的命名空间下写的代码,所以在使用的地方需要增加using Extensions,不然扩展方法用不了。
那这样扩展之后,方法是否能正常执行呢?下面是测试代码和测试结果。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; //Add using Extensions; namespace ExtensionMethod { class Program { static void Main(string[] args) { DirectoryInfoExtensionTest(); Console.ReadLine(); } private static void DirectoryInfoExtensionTest() { DirectoryInfo directory = new DirectoryInfo(@"."); directory.CopyTo(@"F:Tagget",SearchOption.AllDirectories,"*"); } } }
我们可以看到,CopyTo方法正常执行了,这说明扩展成功了。
这是对于系统类进行扩展的。有时我们会遇到只有DLL或者EXE程序集的类库,如第三方类库,这时我们是否可以按同样的方法扩展呢?答案是可以的!下面是一个简单的Log的类库的代码。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Log { public sealed class Record { public void Write(string info) { Console.WriteLine(info); Console.WriteLine("Write to local disk OK!"); } } }
这里以控制台输出的形式表示日志记录在了磁盘上,而有时我们为了方便数据的查询,会希望日志是记录在数据库中,这时我们可以通过扩展方法的形式来扩展。下面是扩展方法的代码
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Log; namespace Extensions { /// <summary> /// Extension Record /// </summary> public static class RecordExtension { public static void WriteDatabase(this Record record, string info) { Console.WriteLine(info); Console.WriteLine("Write to database OK!"); } } }
同样是定义了一个Record的静态扩展类RecordExtension,然后增加WriteDatabase的方法,后面的第一个参数是this开头,之后是要扩展的类Reocrd的类型,再之后的参数info才是真正的传递参数。下面是测试代码和测试结果。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; //Add using Extensions; using Log; namespace ExtensionMethod { class Program { static void Main(string[] args) { DefineClassExtensionTest(); Console.ReadLine(); } private static void DefineClassExtensionTest() { Record record = new Record(); string info="This is my log record!"; record.Write(info); record.WriteDatabase(info); } } }
通过扩展方法的运用,可以在不改变原类的情况下实现扩展。具体也可参照该文《扩展方法(C# 编程指南)》