为什么重载的方法没有被调用?

13
我认为运行时决定调用的方法,不知道我有没有理解错?示例代码:
class Program
{
    static void Main(string[] args)
    {
        var magic = new MagicClass();
        magic.DoStuff(new ImplA());
        magic.DoStuff(new ImplB());
        Console.ReadLine();
    }
}
class MagicClass
{
    internal void DoStuff<T>(T input) where T : SomeBase
    {
        HiThere(input);
    }

    void HiThere(SomeBase input)
    {
        Console.WriteLine("Base impl");
    }

    void HiThere(ImplA input)
    {
        Console.WriteLine("ImplA");
    }

    void HiThere(ImplB input)
    {
        Console.WriteLine("ImplB");
    }
}

abstract class SomeBase
{

}
class ImplA : SomeBase{}
class ImplB : SomeBase{}

我原以为会得到:

ImplA
ImplB

作为输出,但它打印出“Base impl”。有什么办法可以在不强制转换输入的情况下获得重载方法吗?

2
这里有一个相当不错的解释:http://csharpindepth.com/Articles/General/Overloading.aspx - Jakub Kaleta
2个回答

18

重载由编译器选择。对于这里的调用:

internal void DoStuff<T>(T input) where T : SomeBase
{
    HiThere(input);
}

在编译时,it选择拥有SomeBase的对象。

您最可能想要的是重写。这意味着不同的逻辑必须放到SomeBase的继承者中:

abstract class SomeBase
{
  abstract string Name { get; }
}
class ImplA : SomeBase{ override string Name { get { return "ImplA"; } } }
class ImplB : SomeBase{ override string Name { get { return "ImplB"; } } }

void HiThere(SomeBase input)
{
    Console.WriteLine(input.Name);
}

2
另一个可能性是他想要进行双重分派或使用访问者模式 - Matthew Watson
6
或者使用 dynamicHiThere((dynamic)input); 这将重载决议移至运行时,此时已知 input 的确切类型。 - Daniel Hilgarth
@DanielHilgarth:这可能是双重分派的最佳实现方式了。 :) - Matthew Watson
@MatthewWatson:确实,那将是双重分派的具体实现。 - Daniel Hilgarth

4

重载是在编译时选择的。
覆盖是在运行时选择的。

在这里,编译器只知道 T 可以赋值给 SomeBase,但什么也不知道。实际上,如果它按照你的期望工作,你可以完全跳过 where T : SomeBase 部分。你需要它的原因是编译器需要了解那些信息才能检查提供的对象上可以调用什么。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接