扩展方法从简单应用到深入解析,读这一篇文章就够了
前言(扯淡-_-)
大家好,今天和大家聊聊扩展的事,我将带着大家从简单应用开始深入理解扩展方法的原理,并对扩展方法的使用给出合理的建议。
在实际应用中,当我们在使用某类时发现类中缺少我们想要的方法,最简单直接的就是修改类的源代码来添加我们想要的方法。但事实往往不如人意,总会因为各种因素不可以直接修改源码:拿不到源码、不允许修改,这时候通过继承并扩展的方式来复用是再好不过了,但是如果连最后的继承的权利都剥夺的话(密封类不允许继承)?...这时候就需要用到【扩展方法】了。
扩展方法简介
我们先来看看理论(大家不要急,待会就上代码):
扩展方法是C#3.0中引入的新特性,它可以向类中添加新方法,而不需要使用继承来创建新类,也不需要修改原有的类。使用原有类型对象的实例即可直接调用新方法。
它必须符合以下要求(重要):
- 必须是定义在静态类中的静态方法;
- 第一个参数的类型是要扩展的类型;
- 第一个参数需要添加this关键字以标识其为扩展方法。
简单应用
我们用string类型来举个例子:string类中有很多方法,比如常用的a.ToString();
现在我们想给string类添加一个方法,用来获取字符串中包含多少个单词,
先看看这两种做法:
一、直接新建方法(通用,但不是给string类添加新方法,这里写出来用于比较)
public class Tool {
public static int GetWordCount(string str)
{
return str.Split(new char[]{' ',',','?'},StringSplitOptions.RemoveEmptyEntries).Length;
}
}
调用:
string s="myTest string. yeyeye";
int count=Tool.GetWordCount(s);
直接在工具类Tool中建了一个GetWordCount方法,直接传值调用
二、继承的方式(注意:String类不允许被继承这里用来比较)
public class MyString:String {
public int GetWordCount()
{
return this.Split(new char[]{' ',',','?'},StringSplitOptions.RemoveEmptyEntries).Length;
}
}
调用:
MyString s="myTest string. yeyeye";
int count=s.GetWordCount();
三、扩展方法的方式
除了以上两种方法 ,还有什么方式呢?接下来我们来看看不使用继承给string类添加一个方法?
public static class MyExtension {
public static int GetWordCount(this string str)
{
return str.Split(new char[]{' ',',','?'},StringSplitOptions.RemoveEmptyEntries).Length;
}
}
调用:
string s="myTest string. yeyeye";
int count=s.GetWordCount();
如上 ;这就是一个扩展方法,注意红色标记的3个位置,静态类中的静态方法通过this关键字标识类型。
我们来比较一下;
- 第一种方式是比较通用的一种方式,也是通常情况下使用最多的,但和扩展方法比较来说,代码的可读性不如扩展方法。
- 继承的方式是一个很好的扩展方案,但有时不一定是完美的,比如:a. 每次继承都会产生新类,且使用时需要进行相应的强制转换(string=>MyString),b. 有些类本身设计为密封类,是不允许被继承的,如上所使用的例子string是不允许被继承的,所以第二种方案是不可行的。这时,第三种方案扩展方法就派上用场了。
我们再来看看扩展方法的使用:
public static class MyExtension {
public static int GetWordCount(this string str)
{
return str.Split(new char[]{' ',',','?'},StringSplitOptions.RemoveEmptyEntries).Length;
}
}
调用:
string s="myTest string. yeyeye";
int count=s.GetWordCount();、
注意:我们只有引用了MyExtension所在命名空间后才可以使用新扩展的方法。
看到这 ,我相信大家都已经能熟练使用扩展方法了吧,现在我们就来深入理解一下扩展方法的原理。
深入理解扩展方法
下面是摘自CSDN-simonezhlx的分析并进行整理:如下:扩展方法StringToUpper,使用s实例可直接使用新扩展的方法
我们先来看看通过Reflector反编译回来的IL代码:
如你所看到的,IL中的扩展方法调用已经被解释成了一个简单的静态方法调用。这意味着什么?
扩展方法不过是种更简单调用静态方法的手段而已,使代码写起来更自然。换言之,最终编译器还是将扩展方法转化成静态类的静态方法调用
既然编译后转换成了静态方法调用的方式,那么编译器又是如何辨别这是一个扩展方法呢?继续看下面:
可以看到我们写的扩展方法在编译中被解释成了包含指定特性(ExtensionAttribute)的静态方法,该特性使编译器清楚该方法实际上是个扩展方法
我们如何辨别是否是扩展方法?
扩展方法通过如下辨别:
1.蓝色箭头
2.包含(extension)的提示信息
你可能使用过的扩展方法:
如果你使用过Linq,你会发现Linq和扩展方法的关系原来是如此密切!
这么多蓝色箭头,呵呵了....
扩展方法的几点总结
- 可以向类中添加新方法,而不需要使用继承来创建新类,也不需要修改原有的类;
- 如果扩展方法与类中的方法有相同的签名,则扩展方法不会被调用,即:扩展方法会被被扩展类的同名方法覆盖,所以实现扩展方法我们需要承担随时被覆盖的风险(例如:如果扩展一个string类中的ToString(), 这时候扩展方法是无效的);
- 扩展方法不能访问被扩展类的私有成员
- 扩展方法只能使用实例来调用,不能像普通的静态方法一样使用类名调用;
- 只有引入扩展方法所在的命名空间后,扩展方法才可以使用。
扩展方法特性的使用选择
微软公司提供的文档中提到:“通常,建议您只在不得已的情况下才实现扩展方法,并谨慎的实现。只要有可能,必须扩展现有类型的客户端代码都应该通过创建从现有类型派生的新类型来达到这一目的。"
但是,通过我们的一些实例分析来看,扩展方法在某些特定的场合应该是优选的方案。如果你的类库本身功能很完善,能够满足绝大部分的应用,只是针对某个领域、行业或特殊应用进行一系列功能扩展,我们可以使用扩展方法将这些功能进行组织,得到一个专用的扩展方法库,在不改变库代码的情况下为库增加可选的新功能。
这就犹如笔记本电脑的USB一样,不需要将所有的功能点都集成到电脑上,我们只需要将内存,硬盘等大家都需要的功能集成就可以,通过USB扩展其他的新功能设备,通过扩展方法来实现满足对个别用户的需求的功能点。
关于扩展类型就分享到这了,大家在使用时灵活运用就可以了。相关资源获取或其他疑问可在微信公众号CodeL留言。