为什么C#调用错误的重载?

时间:2010-11-09 07:41:32

标签: c# override

我有以下代码,真正 搞笑的东西:

class Parent {
    public virtual void DoSomething(IEnumerable<string> list) {
        Console.WriteLine("Parent.DoSomething(IEnumerable<string>)");
    }
}

class Child : Parent {
    public override void DoSomething(IEnumerable<string> list) {
        Console.WriteLine("Child.DoSomething(IEnumerable<string>)");
    }

    public void DoSomething(IEnumerable<object> list) {
        Console.WriteLine("Child.DoSomething(IEnumerable<object>)");
    }
}

如您所见,DoSomething中的Child方法被正确覆盖。

以下代码的输出非常意外:

...
Child c = new Child();
var list = new List<string> { "Hello", "World!" };
c.DoSomething(list);
...

打印Child.DoSomething(IEnumerable<object>)

Parent c引用... Parent c = new Child(); var list = new List<string> { "Hello", "World!" }; c.DoSomething(list); ... 会产生正确的输出:

Child.DoSomething(IEnumerable<string>)

打印{{1}}

为什么发生?!

3 个答案:

答案 0 :(得分:9)

这是因为C#编译器遵守规范:)

规范说如果在派生类型中声明的任何方法都适用,那么在基类中最初声明的任何方法(即使它们在派生类型中被重写)都会从集合中删除候选人。

现在因为您正在使用C#4(可能),从List<string>IEnumerable<object>进行了隐式转换,因此您的Child.DoSomething(IEnumerable<object>)重载适用,并且编译器从未真正考虑使用IEnumerable<string>

我有一个article about overloading进入这个以及其他一些奇怪的事情。

我建议你不要在类型层次结构中超载 - 这很令人困惑。

答案 1 :(得分:0)

posible soulution,试试:

class Child : Parent {
    public override void DoSomething(IEnumerable<string> list) {
         Console.WriteLine("Child.DoSomething(IEnumerable<string>)");
    }

    public void DoSomething(IEnumerable<object> list) {
        if(list is IEnumerable<string>){
            DoSomething((IEnumerable<string>)list);
            return;
        }
        else  
            Console.WriteLine("Child.DoSomething(IEnumerable<object>)");
    }
}

答案 2 :(得分:0)

@Lasse,

你写了

  

因为编译器只使用它知道的对象的类型,并且它只知道对象支持Parent中的内容,因此在解析方法调用时只有一个方法可用。

我不确定我是否理解你所说的内容,但在第二种情况下,由于实例是Child的一个实例,被调用的方法是虚拟的,因此方法解析在运行时完成而不是编译时间,实际上它是被调用的孩子的方法。

关于方法解决规则。它不应该选择更具体的方法吗?我知道这是现阶段的一个有争议的问题。在3.5和2中,它会选择更具体的方法。