扩展方法可以使对象增加额外的“看似是成员”的方法。
普通的实例类型定义:
public class InstanceClass { public int a = 3; public void instanceMethod() { this.a = 122; } }
扩展方法的定义:
public static class InstanceClassExt { public static void Ext(this InstanceClass _this) { _this.a = 5; } }
分别对成员实例方法和扩展方法进行调用,可以看出,扩展方法可以为类型新增“实例方法”,像普通的实例方法一样调用。
InstanceClass i = new InstanceClass(); i.instanceMethod(); i.Ext();
扩展方法是C#中的语法糖,如果去掉扩展方法的this,就会现出原形,可以这么写
InstanceClass i = new InstanceClass(); i.instanceMethod(); InstanceClassExt.Ext(i);
接下来看看IL部分:
.entrypoint主入口
.method private hidebysig static void Main (string[] args) cil managed { .maxstack 1 .entrypoint .locals init ( [0] class ConsoleApp1.InstanceClass i ) IL_0001: newobj instance void ConsoleApp1.InstanceClass::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: callvirt instance void ConsoleApp1.InstanceClass::instanceMethod() IL_000E: ldloc.0 IL_000F: call void ConsoleApp1.InstanceClassExt::Ext(class ConsoleApp1.InstanceClass) IL_0015: ret }
[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。
.method public hidebysig instance void instanceMethod () cil managed { .maxstack 8 IL_0001: ldarg.0 IL_0002: ldc.i4.s 122 IL_0004: stfld int32 ConsoleApp1.InstanceClass::a IL_0009: ret }
而静态的扩展方法则是通过显式声明的对象变量,来对对象进行操作,并未在类型的域内,无法使用private和protected等受保护的成员。
另外,C#编译器还会给该方法添加一个System.Runtime.CompilerServices.ExtensionAttribute的特性
.method public hidebysig static void Ext (class ConsoleApp1.InstanceClass _this) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0001: ldarg.0 IL_0002: ldc.i4.5 IL_0003: stfld int32 ConsoleApp1.InstanceClass::a IL_0008: ret }
扩展方法与实例成员方法的区别:
- 实例成员方法可以使用类型内其他私有成员,而扩展方法不可以。
- 在IL层的实例方法与静态方法的调用方式不一样,性能可能会有差距。
扩展方法与静态方法的关系:
- 编译后并没有扩展方法的说法,只是增加了System.Runtime.CompilerServices.ExtensionAttribute属性,除此之外就是个普通静态方法。
在这里可以使用扩展方法来为MethodInfo添加判断一个方法是否为扩展方法的方法
public static class MethodExtension { public static bool IsExtension(this System.Reflection.MethodInfo _this) { return Attribute.IsDefined(_this, typeof(System.Runtime.CompilerServices.ExtensionAttribute)); } }
最后:
C#糖真甜
文章评论