我有以下情况:
class Foo { }
class Foo<T> : Foo { }
然后是两种方法
void DoStuff(Foo foo)
{
DoStuffImpl(foo);
}
void DoStuffImpl(Foo foo)
{
Console.WriteLine("A");
}
void DoStuffImpl<T>(Foo<T> foo)
{
Console.WriteLine("B");
}
void Main()
{
DoStuff(new Foo<int>()); // prints A
}
(注意,代码是在浏览器中编写的,但描述了我面临的情况)
如何让它调用泛型方法,然后打印B?
这可以完全没有反思吗?我对如何用反射做一些想法,但我正在寻找一个更清洁的解决方案,如果存在。
注意:我无法使DoStuff
通用,因为它将与WCF一起使用,并且不允许使用开放的泛型类型。
答案 0 :(得分:17)
(我假设您已经明白为什么会发生这种情况。如果没有,请阅读我的overload resolution article并告诉我是否仍然不清楚。)
如果您使用的是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<T> : Foo
{
public override void CallStuffImpl(FooImplType x)
{
// Looks like it's redundant, but isn't! "this" is
// known to be Foo<T> rather than Foo
x.DoStuffImpl(this);
}
}
然后:
void DoStuff(Foo foo)
{
foo.CallStuffImpl(this); // Let it dispatch appropriately
}
答案 1 :(得分:17)
重载分辨率在编译时执行。编译“DoStuff”时,它已经决定调用哪个版本的DoStuffImpl,它根据编译时可用的信息决定,而不是运行时可用的信息。
C#中有四种方法调度:
静态调度在编译时选择静态方法。在运行时,调用所选方法。
实例调度在编译时选择实例方法。在运行时,调用所选方法。 (这是在非虚拟实例方法和使用“base”调用的虚拟方法上使用的调度形式。)
虚拟调度在编译时选择实例方法。在运行时,将调用对象的运行时类型上该方法的大多数重写版本。
动态调度在编译时不执行任何操作(*)。在运行时,编译器再次启动并在运行时进行编译时分析,并生成新的新代码,如果您在编译时首先将其编写,则可以编写该代码。这听起来有点贵;幸运的是,结果被缓存,因此在第二次调用时,您不会再次获得分析和代码的全部费用。
动态调度仅适用于C#4或任何版本的VB。
(*)这不是真的;在某些情况下,即使方法的参数是动态的,编译器也可以在编译时进行分析。细节很复杂。