为什么对泛型类型的转换比C#中的显式转换慢?

为什么对泛型类型的转换比C#中的显式转换慢?,c#,generics,lambda,closures,anonymous-delegates,C#,Generics,Lambda,Closures,Anonymous Delegates,我正在用C#构建一个消息调度图,主要是在玩一些不同的方法。我对我所测量的性能差异感到好奇,但从IL来看,原因并不明显 消息映射: delegate void MessageHandler(Message message); AddHandler(Type t, MessageHandler handler) { /* add 'handler' to messageMap invocation list */ } delegate void GenericMessageHandl

我正在用C#构建一个消息调度图,主要是在玩一些不同的方法。我对我所测量的性能差异感到好奇,但从IL来看,原因并不明显

消息映射:

delegate void MessageHandler(Message message);
AddHandler(Type t, MessageHandler handler) 
{ 
    /* add 'handler' to messageMap invocation list */ 
}

delegate void GenericMessageHandler<T>(T message);
AddHandler<T>(GenericMessageHandler<T> handler) where T: Message
{
    AddHandler(typeof(T), e => { handler((T)e); });
}

Dictionary<Type, MessageHandler> messageMap;
和具有处理程序函数的观察者类:

void HandleVelocityUpdate(VelocityUpdateMessage message) { ... }
我正在测量添加和调用处理程序的两种方法。我正在包装委托调用,以便获得一些概念类型安全性,这就是性能差异

方法1:侦听器调用

AddHandler(typeof(VelocityUpdateMessage), 
           e => { HandleVelocityUpdate((VelocityUpdateMessage)e); });
AddHandler<VelocityUpdateMessage>(HandleVelocityUpdate);
方法2:侦听器调用

AddHandler(typeof(VelocityUpdateMessage), 
           e => { HandleVelocityUpdate((VelocityUpdateMessage)e); });
AddHandler<VelocityUpdateMessage>(HandleVelocityUpdate);
AddHandler(HandleVelocityUpdate);
这两种方法都构建了一个MessageHandler委托,该委托进行了转换和相同的方法调用,但是调用使用方法#2构建的委托稍微慢一点,即使生成的IL看起来相同。转换为泛型类型时是否会有额外的运行时开销?是类型约束吗?我希望一旦解决泛型类型,JITted委托将是相同的


感谢您提供的任何信息。

下面的一行在每次调用匿名类型时都会创建一个新实例。这可能是你表现差异的原因吗

AddHandler(typeof(T), e => { handler((T)e); }); 

好的,我必须查看MethodBody.GetILAsByteArray()IL,而不是ILSpy结果,以便让代理了解到底。使用泛型委托包装我的消息处理程序并强制转换消息类型会生成:

0000 : ldarg.0
0001 : ldfld
0006 : ldarg.1
0007 : unbox.any
000C : callvirt void MessageTest.Message+tMessageHandler`1[MessageTest.VelocityUpdateMessage].Invoke(‌​MessageTest.VelocityUpdateMessage) 
0011 : ret
0000 : ldarg.0
0001 : ldarg.1
0002 : castclass
0007 : call void Message.Component.HandleVelocityUpdate(MessageTest.VelocityUpdateMessage)
000C : ret
其中,具有显式强制转换的包装委托生成:

0000 : ldarg.0
0001 : ldfld
0006 : ldarg.1
0007 : unbox.any
000C : callvirt void MessageTest.Message+tMessageHandler`1[MessageTest.VelocityUpdateMessage].Invoke(‌​MessageTest.VelocityUpdateMessage) 
0011 : ret
0000 : ldarg.0
0001 : ldarg.1
0002 : castclass
0007 : call void Message.Component.HandleVelocityUpdate(MessageTest.VelocityUpdateMessage)
000C : ret

因此,以这种方式使用泛型的开销是最小的。

该行不包含
new{…}
。匿名类型在哪里?为了澄清,我在调用委托(将消息分派给处理程序)时看到了性能差异,我没有分析AddHandler调用,因为它们是设置代码。正如Christopher所说,我将为每种类型获得一个新的方法,但是我的测试代码只是在一个紧密的循环中一遍又一遍地调用同一个处理程序,因此代码生成的成功应该只是一个小问题。也许比我想象的要多。我看到10万个1号呼叫的耗电时间约为70毫秒,而2号呼叫的耗电时间约为110毫秒。它不像调用Delegate.DynamicInvoke那样具有数量级。它不是匿名类型,至少在正式的C语言定义中不是。有一个使lambda表达式工作的隐藏类,新表达式也被隐藏。不过,OP给出了相反的观察结果。Hans Passant是正确的——这不是一种“匿名类型”,因为它不是C#语言特性“匿名类型”的用法。它是一个匿名类型,更一般地说,它是一个编译器生成的类型,具有一个“无法说出的”名称。我们通常称这种类型为“闭包类”或“显示类”。(后者是一个不幸的行话;我们生成的类型包含允许调试器将类型的成员显示为局部变量而不是字段的元素,因此它是“display”类。)您是如何度量的?这对于这些微观优化非常重要。