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

发布于 2024-10-01 11:18:39 字数 1153 浏览 2 评论 0原文

我有以下代码,确实有趣的东西:

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>)");
    }
}

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

以下代码的输出非常出乎意料:

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

打印 Child.DoSomething(IEnumerable)

而将 Parent 引用分配给 c 会生成正确的输出:

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

打印 Child.DoSomething(IEnumerable)

为什么会发生这种情况?!

I have following code that really does funny stuff:

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>)");
    }
}

As you can see, the DoSomething method in Child is overridden correctly.

The output of the following code is very unexpected:

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

Prints Child.DoSomething(IEnumerable<object>)

Whereas assinging a Parent reference to c generates the correct output:

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

Prints Child.DoSomething(IEnumerable<string>)

Why does this happen?!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

望她远 2024-10-08 11:18:39

发生这种情况是因为 C# 编译器遵循规范 :)

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

现在,因为您使用的是 C# 4(大概),所以存在从 ListIEnumerable的隐式转换,因此您的 Child.DoSomething(IEnumerable< object>) 重载是适用的,并且编译器从未真正考虑使用 IEnumerable 的重载。

我有一篇关于重载的文章,其中讨论了这个问题和其他一些奇怪的问题。

我建议你不要跨类型层次结构超载——这会令人困惑。

It happens because the C# compiler obeys the spec :)

The specification says that if any method declared in a derived type is applicable, any methods originally declared in a base class (even if they're overridden in the derived type) are removed from the set of candidates.

Now because you're using C# 4 (presumably) there's an implicit conversion from List<string> to IEnumerable<object> so your Child.DoSomething(IEnumerable<object>) overload is applicable, and the compiler never really considers the one using IEnumerable<string>.

I have an article about overloading which goes into this and some other oddities.

I advise you not to overload across type hierarchies - it's confusing.

旧话新听 2024-10-08 11:18:39

可能的解决方案,尝试:

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>)");
    }
}

posible soulution, try:

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>)");
    }
}
雪花飘飘的天空 2024-10-08 11:18:39

@Lasse,

你写的

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

我不确定我是否理解你在说什么,但在第二种情况下,由于实例是 Child 的实例并且被调用的方法是虚拟的,因此方法解析是在运行时而不是编译时完成的,并且事实上,调用的是子进程的方法。

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

@Lasse,

you wrote

Because the compiler only uses the type of the object that it knows about, and it only knows that the object supports what's in Parent, and thus only has one method available when resolving the method invocation.

I'm not sure if I understand what you're saying, but in the second case, since the instance is an instance of Child and the method being called is virtual, the method resolution is done at run time and not compile time, and in fact it is the child's method that is being called.

As regards method resolution rules. Shouldn't it choose a more specific method? I know it's a moot point at this stage. in 3.5 and 2 it would pick the more specific method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文