扩展方法可以使对象增加额外的“看似是成员”的方法。

普通的实例类型定义:

{{EJS0}}

扩展方法的定义:

{{EJS1}}

分别对成员实例方法和扩展方法进行调用,可以看出,扩展方法可以为类型新增“实例方法”,像普通的实例方法一样调用。

{{EJS2}}

扩展方法是C#中的语法糖,如果去掉扩展方法的this,就会现出原形,可以这么写

{{EJS3}}

 

接下来看看IL部分:

.entrypoint主入口

{{EJS4}}

以下中括号中的数字代表IL指令行号,如[01,06]指的是代码中的IL_0001和IL_0006行

[01,06]在实例化InstanceClass类型后,把该对象存到了局部变量0。

[07,08]首先把局部变量0压入了计算堆栈,使用了callvirt调用了成员实例方法instanceMethod

[0E,0F]然后再把局部变量0压入计算堆栈,使用call来调用扩展静态方法Ext

在这可以看出成员实例方法和静态扩展方法的调用方式是不一样的,因为callvirt是后期绑定的运行时类型调用,所以实例方法的性能可能会比静态扩展方法低。

两种调用之前都压入了一个对象到计算堆栈上,也就是方法中的形参,也就是说,实例方法和静态扩展方法的形参表至少有一个形参。

虽然在实例成员方法中没有声明任何形参,但是代码中还是使用了ldarg.0(LoadArgument 0)加载第一个形参,也就是隐藏参数this。

{{EJS5}}

而静态的扩展方法则是通过显式声明的对象变量,来对对象进行操作,并未在类型的域内,无法使用private和protected等受保护的成员。

另外,C#编译器还会给该方法添加一个System.Runtime.CompilerServices.ExtensionAttribute的特性

{{EJS6}}

 

扩展方法与实例成员方法的区别:

  • 实例成员方法可以使用类型内其他私有成员,而扩展方法不可以。
  • 在IL层的实例方法与静态方法的调用方式不一样,性能可能会有差距。

扩展方法与静态方法的关系:

  • 编译后并没有扩展方法的说法,只是增加了System.Runtime.CompilerServices.ExtensionAttribute属性,除此之外就是个普通静态方法。

 

在这里可以使用扩展方法来为MethodInfo添加判断一个方法是否为扩展方法的方法

{{EJS7}}

 

最后:

C#糖真甜