我一直在考虑是否可以应用DI模式而不会产生虚拟方法调用的成本(根据实验,我做的可能比非虚拟调用慢4倍)。我的第一个想法是通过泛型来进行依赖注入:
sealed class ComponentA<TComponentB, TComponentC> : IComponentA
where TComponentB : IComponentB
where TComponentC : IComponentC
{ ... }
不幸的是,即使TComponentB和TComponentC的具体实现被指定为泛型类型参数并且所有类都被声明为sealed,CLR仍然通过接口进行方法调用。让CLR进行非虚拟调用的唯一方法是将所有类更改为结构(实现接口)。使用struct对DI来说并没有多大意义,并且使得下面的问题更加无法解决。
上述解决方案的第二个问题是它无法处理循环引用。我想不出任何方式,无论是通过C#代码,还是通过构造表达式树来处理循环引用,因为这将需要无限递归泛型类型。 (.Net确实支持引用自身的泛型类型,但它似乎没有推广到这种情况。)因为只有结构可以导致CLR绕过接口,我不认为这个问题是可以解决的,因为之间的循环引用结构可能会引起悖论。
我能想到的只有一个其他解决方案,它保证可以工作 - 在运行时从头开始发出所有类,也许可以将它们作为模板基于编译类。虽然不是一个理想的解决方案。
任何人都有更好的想法?
编辑:关于大多数评论,我想我应该说这是根据“纯粹的求知欲”提出的,我辩论是否要问这个,因为我确实认识到我没有任何具体的案例,其中必要。我只是在考虑它的乐趣,并想知道是否有其他人在此之前遇到过它。
答案 0 :(得分:4)
在我看来尝试完全过度设计的典型例子。只是不要妥协你的设计,因为你可以节省几十毫秒 - 如果它甚至是。
您是否认真地建议,由于callvirt
说明,您的应用最终会明显慢于用户(您为该应用编写的人)会发现任何差异 - at 所有?我非常怀疑。
答案 1 :(得分:3)
此blog post解释了无法优化虚拟呼叫的原因。
答案 2 :(得分:2)
虽然callvirt
指令确实需要更长的时间,但这通常是因为它在调用方法之前为CLR提供了便宜的null
检查。 callvirt
不应该花费比call
指令更长的时间,特别是考虑null
检查。
您是否发现通过创建类型(structs
或具有静态方法的类)可以显着提高应用程序的性能,这些类型允许您保证C#编译器将发出call
指令而不是比callvirt
指示?
我问的原因是我想知道你是否要创建一个难以维护的代码库,这个代码库很脆弱,很难用来解决可能存在或不存在的问题。