C# 拓展方法

小柊 发表于 2018年07月31日 22时41分25秒

一、序

拓展方法是C# 3.0中添加的一个新特性,虽然C# 3.0的推出已经过去很久了,但我还是想把这个拓展方法单独提出来和大伙讲讲——因为它真的真的太棒了!

虽然C#的拓展方法可能在您日常开发中很少会需要自己去写拓展方法,但您在日常的开发工作中,一定会多多少少用到一些库提供的拓展方法,因为有很多库的功能就是基于拓展方法提供的,比如说像.net自带的System.Linq命名空间下,提供的一系列关于集合查询的方法,以及Polly中Policy的定义等也会使用到拓展方法。可见拓展方法已经深深的融入到我们的开发生活中。

 

首先看一下微软官方对拓展方法的介绍:

扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。

 

从上面的介绍可以看出一个拓展方法最重要的一个特点:不需要创建任何新的派生类,即可对指定的原始类型添加方法。

下面我会举一个简单的例子,来解释这个特性有多么的方便。

 

二、情景

现在我有这么一个函数GetString(),它用于从一个字符串中,提取从start字符(串)开始,到end字符(串)结束的子串:

 

在实际情景中,这个方法我经常会使用到,但每次使用的时候都必须这么去调用它:

 

可以看到写起来非常的麻烦,每次都要使用“类名.方法名”的方式去调用它。

不过在这里我想插一句题外话,从C# 6 起,我们可以使用using static语法简化调用:

 

虽然在上面使用了C# 6的using static语法,但我们还是要单独的去使用一个函数“GetString”,我们能不能让这个“GetString”函数成为内置的string类型的实例方法呢?

 

三、以往的解决方法:继承

如果我们要对一个已有的类型添加新的实例方法和属性,通常会创建一个新类,并使这个新类继承于我们想要拓展的已有类,然后再在此新类中添加新的方法,比如下面有个Animal类,我想为Animal类添加一个新的实例方法sleep,我会这么做:

 

可以看到,通过继承并创建新类的方式有很多的问题,随便提两个:

1.创建的新类并不等同于父类,在使用上会有限制:比如赋值给父类类型变量,就不能使用新增的方法了;

 

2.由于密封类不能被继承,所以此方法不能对密封类进行拓展。

 

四、C# 3带来的福音:拓展方法

在C# 3.0中,微软为C#带来了拓展方法这一特性。

拓展方法可以为所有的现有类型添加新的方法,并不需要去使用继承,从而绕过了密封类这一限制。

那么该怎么做呢?

 

非常简单,只要将需要新增的方法放到一个非嵌套非泛型静态类中,成作为一个静态方法,同时将需要拓展的类型作为函数的第一个参数,且此参数定义前加一个“this”就可以了。

拿我们刚刚第二章中的GetString为例,我们只需要将函数定义从

 

改为:

 

就可以了。注意到了吗,在新的函数定义中,第一个参数的定义前多了一个this,就这么简单!

现在我们来看一下效果:

StringHelper类:

 

调用:

 

看,现在我们能在string类型后面直接点出GetString方法了!是不是非常的方便?

 

五、其实还是语法糖

拓展方法这么强大,内部是怎么实现的呢?

其实,这一切还是语法糖,我们用反编译工具反编译一下刚刚的程序,可以看到刚刚的那句

 

编译后的IL代码:

 

追根究底,其实就是一个静态函数调用【摊手】。

 

六、结语:

其实我们常用那些Linq方法,其实都是位于System.Linq命名空间中的拓展方法:

 

拓展方法这个特性一直默默无闻,很多人可能都不知道这个概念,但不要忘记,它们可支起了C#世界的小半边天啊。

 

参考资料:《扩展方法(C# 编程指南)》(https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/extension-methods

 

 

 

小柊

2018年7月31日 22:26:54

相关文章

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注