最近被问到什么是.Net中的委托。问题虽然简单却无从回答。只能说委托是托管世界的函数指针,这么说没啥大毛病,但也都是毛病(当时自己也知道这么说不太对,不过自己不太爱用这个也没准备确实没有更好的答案)。
执行效率正巧前段时间看Core CLR的文档看到不同方式调用函数效率的比较正巧有这个,摘录如下。这段内容在 clr官方文档
的 为什么反射很慢 ?里。
GetViaProperty 0.2159 ns 0.0047 ns 1.00 0.00
GetViaDelegate 1.8903 ns 0.0082 ns 8.82 0.00
GetViaILEmit 2.9236 ns 0.0067 ns 13.64 0.00
GetViaCompiledExpressionTrees 12.3623 ns 0.0200 ns 57.65 0.00
GetViaFastMember 35.9199 ns 0.0528 ns 167.52 0.00
GetViaReflectionWithCaching 125.3878 ns 0.2017 ns 584.78 0.00
GetViaReflection 197.9258 ns 0.2704 ns 923.08 0.01
GetViaDelegateDynamicInvoke 842.9131 ns 1.2649 ns 3,931.17 419.04
Writing a Property (‘Set’) Method Mean StdErr Scaled Bytes Allocated/Op
SetViaProperty 1.4043 ns 0.0200 ns 6.55 0.00
SetViaDelegate 2.8215 ns 0.0078 ns 13.16 0.00
SetViaILEmit 2.8226 ns 0.0061 ns 13.16 0.00
SetViaCompiledExpressionTrees 10.7329 ns 0.0221 ns 50.06 0.00
SetViaFastMember 36.6210 ns 0.0393 ns 170.79 0.00
SetViaReflectionWithCaching 214.4321 ns 0.3122 ns 1,000.07 98.49
SetViaReflection 287.1039 ns 0.3288 ns 1,338.99 115.63
SetViaDelegateDynamicInvoke 922.4618 ns 2.9192 ns 4,302.17 390.99
上表分别列出了读取和设置属性通过不同方式的耗时等结果,我们可以看到直接通过属性读取和通过委托读取速度的平均值相差了接近10倍。这么看委托显然就不是函数指针了(函数指针的性能损失很小),那么下面就具体看下究竟是啥。
解析先上实例代码如下:
internal class HelloWorld { public static void HelloWorld1() { Console.WriteLine("hello world1"); } public delegate void SayHi(); public void Main() { SayHi? helloWorld = new SayHi(HelloWorld1); helloWorld.Invoke(); } }很简单的代码,编译后用ILSpy打开。
元数据与IL首先看下元数据表,毫不例外的在02 TypeDef表里找到了委托对象类型定义,毕竟一切皆对象,这个应该和事件是一个处理方法。
Name BaseType FieldList MethodListSayHi 0x100000E 0x4000000 0x600006
剩下的表暂时先不看了(主要时间太长不记得类型方法在表里是咋对应起来的了)
下面先把类型SayHi的定义相关的IL代码贴出来
.class nested public auto ansi sealed SayHi extends [System.Runtime]System.MulticastDelegate { // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( object 'object', native int 'method' ) runtime managed { } // end of method SayHi::.ctor .method public hidebysig newslot virtual instance void Invoke () runtime managed { } // end of method SayHi::Invoke .method public hidebysig newslot virtual instance class [System.Runtime]System.IAsyncResult BeginInvoke ( class [System.Runtime]System.AsyncCallback callback, object 'object' ) runtime managed { } // end of method SayHi::BeginInvoke .method public hidebysig newslot virtual instance void EndInvoke ( class [System.Runtime]System.IAsyncResult result ) runtime managed { } // end of method SayHi::EndInvoke } // end of class SayHi