2

已经有一些关于这种事情的堆栈溢出帖子,但并不完全相同 - 如果这是已经回答的问题,请提前道歉。

为什么这不起作用:

public class MyBase { }

public class MyUtils
{
    public bool Foo<T> (T myObject) { return true; }
    public bool Foo (MyBase myBaseObject) { return false; }

    public void Go<T> (IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            // this test fails
            Assert.IsFalse (Foo (item));
        }
    }
}

如果我在上面调用 Go() 并传入 MyBase 对象的负载,每次对 Foo 的调用都会调用通用的 Foo(),它返回 true。

new MyUtils ().Go (new MyBase[] { new MyBase (), new MyBase () });      

为什么它不调用专门的 MyBase 版本呢?如果我直接调用 Foo (new MyBase ()) ,它会正确推断出要进行的调用。这是因为 C#3 中的集合缺乏协方差,还是我只是很傻,没有正确地做到这一点?

谢谢!

艾萨克

4

2 回答 2

1

它不会调用“专用”方法,因为编译器会选择在编译程序(在本例中是函数)时调用的方法Go而不是运行时。

当编译器编译该Go函数时,它所拥有的唯一信息是有一些类型为 的对象T。它不知道您可以在以后的某个时间点为它提供 type 的对象MyBase。它唯一的选择是选择Foo<T>重载,因此它将其烘焙到已编译的程序中。

如果您希望应用程序在运行时选择重载,并在应用程序运行时通过查看对象来选择最佳重载,这称为“动态调度”,并且仅用于 Ruby、Python、PHP 等动态语言, ETC。

C#3 是完全静态的,不支持这一点。如果您希望它以这种方式工作,您必须在代码中编写一个 if 语句来检查类型。另一方面,C#4 有一些动态支持。如果您使用 C# 4 编写此代码,则可以如下声明“Go”函数:

 public void Go<T> (IEnumerable<dynamic> items)

然后它会在运行时使用动态调度来选择调用哪个重载,并调用专门的重载来获取MyBase

于 2009-12-06T00:20:30.227 回答
0

根据 C# 规范(7.4.3.2),非泛型方法比泛型方法好,所以应该选择它。

但是,我认为在您的情况下会选择通用的,因为它是在通用调用上下文(“foreach”循环)中调用的。

于 2009-12-03T13:39:48.303 回答