扩展方法可以使对象增加额外的“看似是成员”的方法。
普通的实例类型定义:
{{EJS0}}
扩展方法的定义:
{{EJS1}}
分别对成员实例方法和扩展方法进行调用,可以看出,扩展方法可以为类型新增“实例方法”,像普通的实例方法一样调用。
{{EJS2}}
扩展方法是C#中的语法糖,如果去掉扩展方法的this,就会现出原形,可以这么写
{{EJS3}}
接下来看看IL部分:
.entrypoint主入口
{{EJS4}}
[01,06]在实例化InstanceClass类型后,把该对象存到了局部变量0。 [07,08]首先把局部变量0压入了计算堆栈,使用了callvirt调用了成员实例方法instanceMethod [0E,0F]然后再把局部变量0压入计算堆栈,使用call来调用扩展静态方法Ext以下中括号中的数字代表IL指令行号,如[01,06]指的是代码中的IL_0001和IL_0006行
在这可以看出成员实例方法和静态扩展方法的调用方式是不一样的,因为callvirt是后期绑定的运行时类型调用,所以实例方法的性能可能会比静态扩展方法低。
两种调用之前都压入了一个对象到计算堆栈上,也就是方法中的形参,也就是说,实例方法和静态扩展方法的形参表至少有一个形参。
虽然在实例成员方法中没有声明任何形参,但是代码中还是使用了ldarg.0(LoadArgument 0)加载第一个形参,也就是隐藏参数this。
{{EJS5}}
而静态的扩展方法则是通过显式声明的对象变量,来对对象进行操作,并未在类型的域内,无法使用private和protected等受保护的成员。
另外,C#编译器还会给该方法添加一个System.Runtime.CompilerServices.ExtensionAttribute的特性
{{EJS6}}
扩展方法与实例成员方法的区别:
- 实例成员方法可以使用类型内其他私有成员,而扩展方法不可以。
- 在IL层的实例方法与静态方法的调用方式不一样,性能可能会有差距。
扩展方法与静态方法的关系:
- 编译后并没有扩展方法的说法,只是增加了System.Runtime.CompilerServices.ExtensionAttribute属性,除此之外就是个普通静态方法。
在这里可以使用扩展方法来为MethodInfo添加判断一个方法是否为扩展方法的方法
{{EJS7}}
最后:
C#糖真甜