在IEnumerable <t> </t>上键入推断

时间:2009-12-02 18:59:46

标签: generics resolution covariance

关于此类事情已经有一些关于堆栈溢出的帖子但不完全相同 - 所以如果这已经得到解答,请提前道歉。

为什么这不起作用:

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()),它会正确地推断出make的调用。这是因为C#3中的集合缺乏协方差,还是我只是愚蠢而没有正确地做到这一点?

谢谢!

艾萨克

2 个答案:

答案 0 :(得分:1)

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

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

如果您希望应用程序在运行时选择重载,并通过在应用程序运行时查看对象来选择最佳重载,那称为“动态分派”,并且仅由Ruby等动态语言使用, Python,PHP等

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

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

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

答案 1 :(得分:0)

根据C#规范(7.4.3.2),非泛型方法优于通用方法,因此应选择它。

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