DotNet 3.0+ 新特性学习 之 扩展方法(Extension Method)
定义:
扩展方法允许对现存已编译的类型(例如 类,结构,接口)在不需要直接更新的情况下进行方法的扩展。
理解:
1) 扩展方法是对已编译类型的扩展。何为已编译类型? 说通俗点就是已经编译好的DLL或者exe。
2) 扩展方法不需要直接更新已编译类型。一般来说,一个已编译好的DLL,你是不知道它的具体实现的,只是在引用它以后,能知道它的public方法 (用Reflector查看代码除外)。当你需要针对某个已引用的DLL在自己的项目中进行扩展,怎么办。扩展方法就是解决这类问题的。
3) 扩展方法是对方法的扩展。换而言之,它不是对已编译类型的扩展。通俗点就是给已编译的类型增加一些方法。
4) 扩展方法不仅可以给某个类增加方法,还可以给结构,接口增加方法。有人可能觉得我给一个类增加方法的话可以使用继承。可以,没有问题。但是如果遇到seal ed class怎么办呢?
好了,啰嗦了一堆,大家肯定等急了吧。赶快来看咋用的吧。
实现:
1. 扩展方法 --- 类
比如,我们需要给string类型加一个扩展方法,用来将string转化成int 数组。嘻嘻,熟悉string的同学们都知道,string本身并没有一个方法来直接转化成一个数组。 好了,开工了,请看下面的代码:
namespace ExtensionMethod { public static class ExtensionForClass { public static int[] ToIntArray(this string str) { // To do } } } |
铛铛铛铛~~ 神奇的事情发生了,当你在你的项目中引用ExtensionMethod 命名空间,在一个string变量后面点“.”,你就会发现一个ToIntArray()的方法。见下图
看见了吧。好神奇闹~~~.在此时,我们默认的数组分隔符是“,”,如果分隔符不定,那么我们可以重载此扩展方法,如下
namespace ExtensionMethod { public static class ExtensionForClass { public static int[] ToIntArray(this string str) { // To do
return default(int[]); } /// <summary> /// Overload extension method ToIntArray /// </summary> /// <param name="str">source string</param> /// <param name="separator">Separator</param> /// <returns>Int Array</returns> public static int[] ToIntArray(this string str, char separator) { // To do
return default(int[]); } } } |
恩, 搜噶,这样,我们就可以看到该方法的2个重载了,如下图
- 扩展方法 --- 结构
结构,结构??恩~~.net类库里现成的结构是啥呢?哦,对了,DateTime (别说你到现在还以为它是一个类哈)。比如说,有些数据库设计来说,把DateTime存成一个int的数,这样,在查找的时候,效率会很高,当然了,比较int要比比较DateTime要快呢。那么,我们在前台代码处理的时候,给DateTime加一个扩展方法,让它直接转化成Int。不多说废话,看下面代码
namespace ExtensionMethod { public static class ExtentionForStruct { public static int ToInt(this DateTime dateTime) { // To do; } } } |
奇迹再次发生,如下图:
瞧见了吗?嘿嘿。
- 扩展方法 --- 接口
扩展接口?这是个令人深思的话题,先不说为啥,先看代码
namespace ExtensionMethod { public interface ICount { int Add(int x, int y); }
public static class ExtensionInterface { public static int Subtract(this ICount count, int x, int y) { return x - y; } }
} |
看出问题了吗?啥?没有。哈哈,我第一次也没有看出来。大家想想,接口的一个特性是啥?就是接口并不自己实现方法,必须由实现该接口的类来实现方法,对不啦?但是,此时,对接口的扩展方法,已经实现了Subtract方法。这就对接口的本质造成了破坏。可能有人会问,那不能写成这样?
public static int Subtract(this ICount count, int x, int y); |
呵呵,have a try,看看会出现啥?(读者作业)。
怎么用呢?
namespace ExtensionMethod { class Program { static void Main(string[] args) { ICount count = new MyCount();
count.Subtract(5, 4); } }
class MyCount : ICount { public int Add(int x, int y) { return x + y; } }
} |
这回看明白了,由于只是对接口的一个方法的扩展,且已经有了实现,所以,对实现接口的类就不会强迫其实现”Substract”方法。 但是,在使用接口的时候,Substract 却切切实实的在哪呢。
小贴士:
个人还是建议少用对接口的扩展方法,因为这毕竟破坏了面向对象的本质,也不可能对实现接口的类进行行为上的限制。
约束
1) 扩展方法必须放在static 类里面,且本身也是static的。
2) 扩展方法的第一参数必须是你要扩展的类型,this + 类型 + 参数名。
3) 每一个扩展方法之可以被内存中正确的实例调用,或者通过其所处的静态类调用。
4) 扩展方法不可以直接访问扩展类型的成员。
比如
public class Car { public int Speed { get; set; }
public void SpeedUp() { Speed++; } }
public static class CarExtension { public static void SpeedDown(this Car car) { // It's wrong // Speed--;
// It's right car.Speed--; } } |
扩展方法的本质
先看如下代码
class Program { static void Main(string[] args) { ICount count = new MyCount();
// They are same count.Subtract(5, 4);
ExtensionInterface.Subtract(count, 5, 4); } } |
哈哈,被骗了吧,其实是微软玩的一个障眼法。其实它就是 “ExtensionInterface.Subtract(count, 5, 4);”。
个人建议
呼,总算写完了,个人一点点建议哈
1) 扩展方法的命名要遵循所扩展类型的其他方法的命名习惯。
2) 相关类型的扩展方法放到相关类型的命名空间下,在加一个Extension。比如关于System.Data的扩展方法的命名空间就是System.Data.Extension。