上一篇随笔 .NET 扩展方法 (一) 已经对 扩展方法有了大致的介绍,这篇算是一个补充,让我们来看一下扩展方法的几个细节:

一、扩展方法具有继承性

当使用扩展方法扩展一个类型的时候,其也扩展了派生类,所以上一篇的遗留问题“如果给object添加一个扩展方法会出现什么效果呢?” 的

答案就是——所有类型都将扩展该方法。object类已经经受住了时间的考验,我们似乎也找不到更合适的理由来扩展object类。从另外的

角度考虑,如果扩展了object类,很有可能会给“智能敏感提示”造成污染,以至于填充了过多的垃圾信息(因为许多类型也根本用不到该方法)。

二、扩展方法允许和扩展类型原有方法相等效的方法存在 (此处是个雷)

由于汉语语言表述的所带来的不易理解性,我们还是直接用代码来解释吧,如下的代码片段:

    public static class StringExtentsion
{
public static string ToString(this string str)
{
return "Extentsion" + str;
}
} class Program
{
static void Main(string[] args)
{
string str = "test"; Console.WriteLine(str.ToString()); // 输出结果为: test,也就说编译器会优先选用原有类的实例化方法,如果没找到匹配方法再寻找扩展方法 Console.Read();
}
}

由上述的代码片段可以知:StringExtentsion类中扩展方法ToString 和 String类的原有的ToString方法 对于客户端代码而言,它们的语法表象是

一样的,但本质上一个是StringExtentsion类的静态方法,一个是String类的实例化方法。然而编译运行没有产生错误,更没有产生警告。所以在

这种情况下很容“埋雷”,一不小心就会中招。有人也许会说:我注意一下不要和.NET类库的方法重名就可以了。但是你能保证 .NET 6、甚至.NET 10

的方法名和你写的绝对不重名吗?所以,扩展方法存在着版本控制的问题

 

三、扩展方法允许在两个或多个类中共存相同的扩展方法

    public static class StringExtentsion
{
public static bool IsEmpty(this string str)
{
return string.IsNullOrWhiteSpace(str);
}
} public static class OtherStringExtentsion
{
public static bool IsEmpty(this string str)
{
return string.IsNullOrWhiteSpace(str);
}
} class Program
{
static void Main(string[] args)
{
string str = null; //bool result = str.IsEmpty(); // 这种写法会产生编译错误:不明确的调用,因为它能找到两个扩展方法 bool result = StringExtentsion.IsEmpty(str); // 必须显示使用类调用静态方法 Console.WriteLine(result); Console.Read();
}
}

  

四、扩展其他类型的方法

扩展方法不仅可以扩展类类型,而且可以扩展接口、枚举、委托、结构、数组及对应的泛型类型 等类型。

在这里着重说一下扩展接口,任何 “实现了接口的类型对象” 都可以调用接口上的扩展方法。针对这一特点,我们完全可以将实现接口

的共用代码放进扩展方法里,实现代码复用。从形式上来看,该特点是 “单继承 + 接口” 与 “多继承”的中间产物,已经有了“多继承”的影子。

五、委托与扩展方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace MethodDemo
{
public static class StringExtentsion
{
public static string ShowString(this string str)
{
return "ShowString:" + str;
}
} class Program
{
static void Main(string[] args)
{
string str = "meng"; Func<string> fun = str.ShowString; fun(); Func<string> fun2 = str.ToString; fun2(); Console.Read();
}
}
}

  

如果你已经看过了 .NET 扩展方法 (一) ,也许你能猜到我接下来想说什么了吧。没错,看一下IL代码吧,看看编译器在背后搞了哪些“怪”。

顺便借助这个机会,说几个IL指令。

 .method private hidebysig static void  Main(string[] args) cil managed
{
.entrypoint
// 代码大小 55 (0x37)
.maxstack // 初始化3个局部变量
.locals init ([] string str,
[] class [mscorlib]System.Func`<string> fun,
[] class [mscorlib]System.Func`<string> fun2)
IL_0000: nop //将 "meng" 这个字符串对象的引用 入栈
IL_0001: ldstr "meng" // 将栈顶的值赋值给第0个局部变量(即 str),栈顶值出栈
IL_0006: stloc. // 将第0个局部变量入栈 (即 str 入栈)
IL_0007: ldloc. // 将 MethodDemo.StringExtentsion类的静态方法ShowString的指针入栈
IL_0008: ldftn string MethodDemo.StringExtentsion::ShowString(string) // 调用构造函数 new一个 Func<String>类型的委托
IL_000e: newobj instance void class [mscorlib]System.Func`<string>::.ctor(object,
native int)
IL_0013: stloc.
IL_0014: ldloc. // 调用fun对象的Invoke方法
IL_0015: callvirt instance ! class [mscorlib]System.Func`<string>::Invoke()
IL_001a: pop
IL_001b: ldloc.
IL_001c: dup // 将str 对象的实例化方法ToString方法的指针入栈
IL_001d: ldvirtftn instance string [mscorlib]System.Object::ToString()
IL_0023: newobj instance void class [mscorlib]System.Func`<string>::.ctor(object,
native int)
IL_0028: stloc.
IL_0029: ldloc.
IL_002a: callvirt instance ! class [mscorlib]System.Func`<string>::Invoke()
IL_002f: pop
IL_0030: call int32 [mscorlib]System.Console::Read()
IL_0035: pop
IL_0036: ret
} // end of method Program::Main

核心的IL指令已经给予了差不多的注释,根据IL指令可以得出结果:我们又被编译器“欺骗”了一次,fun对象保存的方法指针是

MethodDemo.StringExtentsion类的静态方法ShowString的指针。fun2对象保存的方法的指针str对象的ToString方法的指针。

.NET 扩展方法 (二)的更多相关文章

  1. jQuery扩展方法笔记

    一.方式列表: 1.jQuery.extend(Object); // jQuery 本身的扩展方法 2.jQuery.fn.extend(Object); // jQuery 所选对象扩展方法 二. ...

  2. .NET中那些所谓的新语法之二:匿名类、匿名方法与扩展方法

    开篇:在上一篇中,我们了解了自动属性.隐式类型.自动初始化器等所谓的新语法,这一篇我们继续征程,看看匿名类.匿名方法以及常用的扩展方法.虽然,都是很常见的东西,但是未必我们都明白其中蕴含的奥妙.所以, ...

  3. ASP.Net MVC开发基础学习笔记:二、HtmlHelper与扩展方法

    一.一个功能强大的页面开发辅助类—HtmlHelper初步了解 1.1 有失必有得 在ASP.Net MVC中微软并没有提供类似服务器端控件那种开发方式,毕竟微软的MVC就是传统的请求处理响应的回归. ...

  4. Jquery自定义扩展方法(二)--HTML日历控件

    一.概述 研究了上节的Jquery自定义扩展方法,自己一直想做用jquery写一个小的插件,工作中也用到了用JQuery的日历插件,自己琢磨着去造个轮子--HTML5手机网页日历控件,废话不多说,先看 ...

  5. artDialog学习之旅(二)之扩展方法详解

    名称 描述 核心方法 art.dialog.top 获取artDialog可用最高层window对象.这与直接使用window.top不同,它能排除artDialog对象不存在已经或者顶层页面为框架集 ...

  6. linux系统下php安装mbstring扩展的二种方法

    .执行 复制代码代码如下: yum install php-mbstring 2. 修改php.ini (这一步非常重要, 部分lxadmin版本无法自动修改) 复制代码代码如下: echo ‘ext ...

  7. 23.C#Queryable的扩展方法(十二章12.1-12.2)

    今天要写的知识还真心有点绕呢,对于第一节的内容,其实是把原先在内存中的数据源,换成了从数据库中提取出来的数据.从代码的使用方式上是一样的,直接跳过,来看看IEnumerable和IQueryable的 ...

  8. MVC分页控件之二,为IQueryable定义一个扩展方法,直接反回PagedList<T>结果集(转)

    namespace Entity { public interface IPagedList { /// <summary> /// 记录数 /// </summary> in ...

  9. c#扩展方法的理解(二:接口)

    namespace ExtensionInterfaceMethod { class Program { static void Main(string[] args) { //使用接口变量来调用扩展 ...

随机推荐

  1. 为首次部署MongoDB做好准备:容量计划和监控

    如果你已经完成了自己新的MongoDB应用程序的开发,并且现在正准备将它部署进产品中,那么你和你的运营团队需要讨论一些关键的问题: 最佳部署实践是什么? 为了确保应用程序满足它所必须的服务层次我们需要 ...

  2. Hadoop学习笔记—7.计数器与自定义计数器

    一.Hadoop中的计数器 计数器:计数器是用来记录job的执行进度和状态的.它的作用可以理解为日志.我们通常可以在程序的某个位置插入计数器,用来记录数据或者进度的变化情况,它比日志更便利进行分析. ...

  3. MapReduce剖析笔记之二:Job提交的过程

    上一节以WordCount分析了MapReduce的基本执行流程,但并没有从框架上进行分析,这一部分工作在后续慢慢补充.这一节,先剖析一下作业提交过程. 在分析之前,我们先进行一下粗略的思考,如果要我 ...

  4. Fig 应用编排

    Fig是Docker的应用编排工具,主要用来跟 Docker 一起来构建基于 Docker 的复杂应用,Fig 通过一个配置文件来管理多个Docker容器,非常适合组合使用多个容器进行开发的场景. 说 ...

  5. 【requireJS源码学习01】了解整个requireJS的结构

    前言 现在工作中基本离不开requireJS这种模块管理工具了,之前一直在用,但是对其原理不甚熟悉,整两天我们来试着学习其源码,而后在探寻其背后的AMD思想吧 于是今天的目标是熟悉requireJS整 ...

  6. Atitit.软件开发的几大规则,法则,与原则Principle v3

    Atitit.软件开发的几大规则,法则,与原则Principle  v31.1. 修改历史22. 设计模式六大原则22.1. 设计模式六大原则(1):单一职责原则22.2. 设计模式六大原则(2):里 ...

  7. WCF 安全性 之 Windows

    案例下载 http://download.csdn.net/detail/woxpp/4113172 服务端配置代码 <system.serviceModel> <services& ...

  8. 神奇的CSS sprites,制作特效的新方法

    本文主要内容简译自Dava Shea的英文文章 CSS Sprites: Image Slicing’s Kiss of Death,如果觉得博主讲的含糊不清的话,可以看作者原文. 熟悉了常规切图的我 ...

  9. js高程读书笔记(第4章--变量、作用域和内存)

    JavaScript变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已.由于不存在定义某个变量必须要保存何总数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变. 1. ...

  10. 前端:图文混排-怎么在不使用float的情况下实现想要的效果呢?

    异常处理汇总-前端系列 http://www.cnblogs.com/dunitian/p/4523015.html 举个例子 重点:display:flex (参考:http://www.360do ...