通用重载决议

时间:2010-09-09 16:50:11

标签: c# generics overloading

我有以下情况:

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一起使用,并且不允许使用开放的泛型类型。

2 个答案:

答案 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。

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