为什么IEnumerable<T>被定义为IEnumerable<out T>,而不是IEnumerable<T>?

16

我在浏览MSDNIEnumerable<T>接口定义时发现:

可能是重复问题:
为什么在C# 4中将IEnumerable<T>协变?


public interface IEnumerable<out T> : IEnumerable

我在想为什么T被定义为out,而不是其他。
public interface IEnumerable<T> : IEnumerable

这是什么原因?

因为接口是协变的。 - Jodrell
1
请查看此处:https://dev59.com/9mw15IYBdhLWcg3wQpbS - Likurg
协变性和逆变性 - jrummell
4个回答

21

更多信息可以在这里找到。

out 使类型参数协变,也就是说,你可以使用该类型或任何派生类型。注意,out 只在泛型中以这种方式工作,在方法签名中使用时意义不同(尽管您可能已经知道这一点)。

这里是从引用页面中提取的例子:

// Covariant interface. 
interface ICovariant<out R> { }

// Extending covariant interface. 
interface IExtCovariant<out R> : ICovariant<R> { }

// Implementing covariant interface. 
class Sample<R> : ICovariant<R> { }

class Program
{
    static void Test()
    {
        ICovariant<Object> iobj = new Sample<Object>();
        ICovariant<String> istr = new Sample<String>();

        // You can assign istr to iobj because 
        // the ICovariant interface is covariant.
        iobj = istr;
    }
}

正如你所看到的,接口签名中的 out 允许你将 ICovariant<String> 赋值给 ICovariant<Object> 变量,因为 String 是从 Object 派生而来的。如果没有 out 关键字,你将无法这样做,因为类型会不同。

关于协变性(以及相关的逆变性),你可以在这里阅读更多信息。

正如其他答案所指出的一样,在 .NET 4 中才使 IEnumerable 成为协变。尝试编写以下代码是行不通的:

IEnumerable<Object> strings = new List<string>();

这段代码可以在 .NET 4 及以后的版本中编译通过,但无法在之前的版本中编译通过。


7
< p > out 类型参数说明符表示协变。

< p > 实际应用中,

< p > 如果我定义了两个接口。

interface ISomeInterface<T>
{
}

interface ISomeCovariantInterface<out T> 
{
}

然后,我这样实现它们。
class SomeClass<T> : ISomeInterface<T>, ISomeCovariantInterface<T>
{
}

接下来我尝试编译这段代码,

ISomeCovariantInterface<object> covariant = new SomeClass<string>(); // works
ISomeInterface<object> invariant = new SomeClass<string>(); // fails

// Cannot implicitly convert type 'SomeClass<string>' to 'ISomeInterface<object>'.
// An explicit conversion exists (are you missing a cast?)

这是因为协变接口允许更多的派生实例,而标准接口不允许。 点此查看示例

1
+1 我认为这是最清晰的入门级别答案。 - TarkaDaal

6

协变。这允许将一个集合分配给比其泛型参数中指定的更具体或派生类型的项。

IEnumerable<T>并不总是协变的;这是 .NET 4 中的新功能,更改的原因在这里解释了。


0
为了实现这个目标:
class Base {}
class Derived : Base {}

List<Derived> list = new List<Derived>();
IEnumerable<Base> sequence = list;

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