通用重载决议

我有以下情况:

class Foo { } class Foo : Foo { } 

然后是两种方法

 void DoStuff(Foo foo) { DoStuffImpl(foo); } void DoStuffImpl(Foo foo) { Console.WriteLine("A"); } void DoStuffImpl(Foo foo) { Console.WriteLine("B"); } void Main() { DoStuff(new Foo()); // prints A } 

(注意,代码是在浏览器中编写的,但描述了我面临的情况)

如何让它调用generics方法,然后打印B?

这可以在没有反思的情况下完成吗? 关于如何用reflection完成,我有一些想法,但我正在寻找一个更清洁的解决方案,如果存在的话。

注意:我不能使DoStuffgenerics,因为它将与WCF一起使用,并且不允许打开generics类型。

(我假设你已经明白为什么会发生这种情况。如果没有,请阅读我的重载决议文章并告诉我它是否仍然不清楚。)

如果您使用的是C#4,则可以使用动态类型:

 void DoStuff(Foo foo) { dynamic d = foo; DoStuffImpl(d); } 

注意这不仅仅有一个动态参数 – 想法是通过将foo限制为Foo类型或子类,我们总是有一个有效的DoStuffImpl来调用…它只是确定最好的方法在执行时,不是编译时间。

如果你被困在C#4之前,你可以通过双重调度来实现:

 class Foo { public virtual void CallStuffImpl(FooImplType x) { x.DoStuffImpl(this); } } class Foo : Foo { public override void CallStuffImpl(FooImplType x) { // Looks like it's redundant, but isn't! "this" is // known to be Foo rather than Foo x.DoStuffImpl(this); } } 

然后:

 void DoStuff(Foo foo) { foo.CallStuffImpl(this); // Let it dispatch appropriately } 

过载分辨率在编译时执行。 编译“DoStuff”时,它已经决定调用哪个版本的DoStuffImpl,它根据编译时可用的信息决定,而不是运行时可用的信息。

C#中有四种方法调度:

  • 静态调度在编译时选择静态方法。 在运行时,调用所选方法。

  • 实例调度在编译时选择实例方法。 在运行时,调用所选方法。 (这是在非虚拟实例方法和使用“base”调用的虚拟方法上使用的调度forms。)

  • 虚拟调度在编译时选择实例方法。 在运行时,将调用该对象的运行时类型上该方法最重写版本

  • 动态调度在编译时不执行任何操作(*)。 在运行时,编译器再次启动并在运行时进行编译时分析,并生成新的新代码,如果您在编译时首先将其编写完成,那么这些代码将会被编写。 这听起来有点贵; 幸运的是,结果被缓存,因此在第二次调用时,您不会再次获得所有分析和代码的成本。

动态调度仅适用于C#4或任何版本的VB。

(*)这不完全正确; 在某些情况下,即使方法的参数是动态的,编译器也可以在编译时进行分析。 细节很复杂。