.NET 扩展方法 (二)

上一篇随笔 .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  2

  // 初始化3个局部变量
  .locals init ([0] string str,
           [1] class [mscorlib]System.Func`1<string> fun,
           [2] class [mscorlib]System.Func`1<string> fun2)
  IL_0000:  nop

  //将 "meng" 这个字符串对象的引用 入栈
  IL_0001:  ldstr      "meng"

  // 将栈顶的值赋值给第0个局部变量(即 str),栈顶值出栈
  IL_0006:  stloc.0

  // 将第0个局部变量入栈 (即 str 入栈)
  IL_0007:  ldloc.0

  // 将 MethodDemo.StringExtentsion类的静态方法ShowString的指针入栈
  IL_0008:  ldftn      string MethodDemo.StringExtentsion::ShowString(string)

  // 用栈顶的两个值做参数,调用构造函数 new一个 Func<String>类型的委托
  IL_000e:  newobj     instance void class [mscorlib]System.Func`1<string>::.ctor(object,
                                                                                  native int)
  IL_0013:  stloc.1
  IL_0014:  ldloc.1

  // 调用fun对象的Invoke方法
  IL_0015:  callvirt   instance !0 class [mscorlib]System.Func`1<string>::Invoke()
  IL_001a:  pop
  IL_001b:  ldloc.0
  IL_001c:  dup

  // 将str 对象的实例化方法ToString方法的指针入栈
  IL_001d:  ldvirtftn  instance string [mscorlib]System.Object::ToString()
  IL_0023:  newobj     instance void class [mscorlib]System.Func`1<string>::.ctor(object,
                                                                                  native int)
  IL_0028:  stloc.2
  IL_0029:  ldloc.2
  IL_002a:  callvirt   instance !0 class [mscorlib]System.Func`1<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方法的指针。

时间: 2024-10-18 12:55:28

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

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

一.概述 研究了上节的Jquery自定义扩展方法,自己一直想做用jquery写一个小的插件,工作中也用到了用JQuery的日历插件,自己琢磨着去造个轮子--HTML5手机网页日历控件,废话不多说,先看看效果图吧 效果图很简单,代码封装在JQuery中,网页端只需要要调用即可: 二.Jquery自定义实体对象 Jquery可以自定义函数function,有没有可以定义实体对象,里面封装方法那?查询了一下资料发现,是可以的,不仅能够封装属性,还可以写自己的方法,调用模板代码如下: $.Calende

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

名称 描述 核心方法 art.dialog.top 获取artDialog可用最高层window对象.这与直接使用window.top不同,它能排除artDialog对象不存在已经或者顶层页面为框架集的情况这是iframe应用工具集中的核心方法,你可以用它来操作父页面对象(包括上面的对话框) art.dialog.data(name, value) 跨框架数据共享写入接口.框架与框架之间以及与主页面之间进行数据交换是非常头疼的事情,常规情况下你必须知道框架的名称才能进行数据交换,如果是在复杂的多

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

.执行 复制代码代码如下: yum install php-mbstring 2. 修改php.ini (这一步非常重要, 部分lxadmin版本无法自动修改) 复制代码代码如下: echo ‘extension=mbstring.so' >>/etc/php.ini #更具php安装目录而定 3. 重启web service 如果是apache: service httpd restart 方法二:php 5.36安装目录:/usr/local/php 复制代码代码如下: #cd /usr/

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

一.一个功能强大的页面开发辅助类—HtmlHelper初步了解 1.1 有失必有得 在ASP.Net MVC中微软并没有提供类似服务器端控件那种开发方式,毕竟微软的MVC就是传统的请求处理响应的回归.所以抛弃之前的那种事件响应的模型,抛弃服务器端控件也理所当然. 但是,如果手写Html标签效率又比较低,可重用度比较低.这时,我们该怎样来提高效率呢?首先,经过上篇我们知道可以通过ViewData传递数据,于是我们可以写出以下的Html代码: <input name="UserName&quo

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

开篇:在上一篇中,我们了解了自动属性.隐式类型.自动初始化器等所谓的新语法,这一篇我们继续征程,看看匿名类.匿名方法以及常用的扩展方法.虽然,都是很常见的东西,但是未必我们都明白其中蕴含的奥妙.所以,跟着本篇的步伐,继续来围观. /* 新语法索引 */ 1.自动属性 Auto-Implemented Properties 2.隐式类型 var 3.参数默认值 和 命名参数 4.对象初始化器 与 集合初始化器 { } 5.匿名类 & 匿名方法 6.扩展方法 7.系统内置委托 Func / Acti

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

今天要写的知识还真心有点绕呢,对于第一节的内容,其实是把原先在内存中的数据源,换成了从数据库中提取出来的数据.从代码的使用方式上是一样的,直接跳过,来看看IEnumerable和IQueryable的区别. 正如Enumerable类型包含着关于IEnumerable<T>的扩展方法来实现LINQ标准查询操作符一样,Queryabl类型包含着关于IQueryable<T>的扩展方法.IEnumerable<T>和IQueryable<T>有两个重要的区别.

C#编程(二十一)----------扩展方法

C#中的扩展方法 有许多扩展类的方式.如果有类的源代码,继承就是给类添加功能的好方法.但是如果没有源代码,怎么办?吃屎可以使用扩展方法,它允许改变一个类,但不需要该类的源代码.扩展方法是静态方法,它是类的一部分,但实际上没有放在类的源代码中.例如我有一个Money类,不知道源代码,需要一个AddToAmount(decimal amountToAdd). 我们可以这么做: namespace Wrox { public static class MoneyExtension { public s

c# 扩展方法奇思妙用基础篇五:Dictionary&lt;TKey, TValue&gt; 扩展

Dictionary<TKey, TValue>类是常用的一个基础类,但用起来有时确不是很方便.本文逐一讨论,并使用扩展方法解决. 向字典中添加键和值 添加键和值使用 Add 方法,但很多时候,我们是不敢轻易添加的,因为 Dictionary<TKey, TValue>不允许重复,尝试添加重复的键时 Add 方法引发 ArgumentException. 大多时候,我们都会写成以下的样子: var dict = new Dictionary<int, string>()

C#的扩展方法解析

在使用面向对象的语言进行项目开发的过程中,较多的会使用到"继承"的特性,但是并非所有的场景都适合使用"继承"特性,在设计模式的一些基本原则中也有较多的提到. 继承的有关特性的使用所带来的问题:对象的继承关系实在编译时就定义好了,所以无法在运行时改变从父类继承的实现.子类的实现与它父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化.当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写它或被其他更适合的类替换,这种依赖关系限制