【发布时间】:2012-09-14 12:25:28
【问题描述】:
我正在查看MSDN 的IEnumerable<T> 接口定义,然后查看:
public interface IEnumerable<out T> : IEnumerable
我想知道为什么 T 被定义为 out,为什么不呢?
public interface IEnumerable<T> : IEnumerable
这是什么原因?
【问题讨论】:
我正在查看MSDN 的IEnumerable<T> 接口定义,然后查看:
public interface IEnumerable<out T> : IEnumerable
我想知道为什么 T 被定义为 out,为什么不呢?
public interface IEnumerable<T> : IEnumerable
这是什么原因?
【问题讨论】:
更多信息可以在here找到。
out 使类型参数协变。也就是说,您可以使用类型或任何派生类型。请注意,out 仅适用于泛型,在方法签名中使用时具有不同的含义(尽管您可能已经知道)。
这是取自referenced page的示例:
// 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 关键字,您将无法执行此操作,因为类型会有所不同。
您可以阅读更多关于协变(以及相关的逆变)here。
正如其他答案所指出的,IEnumerable 仅在 .NET 4 中成为协变的。尝试编写如下代码:
IEnumerable<Object> strings = new List<string>();
将在 .NET 4 及更高版本中编译,但在以前的版本中不编译。
【讨论】:
out 类型参数说明符表示协方差。
在实践中,
如果我定义了两个接口。
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?)
这是因为协变接口允许更多派生实例,而标准接口则不允许。
【讨论】:
Covariance. 这允许为集合分配比其通用参数中指定的更具体或派生类型的项。
IEnumerable<T> 并不总是协变的;这是 .NET 4 的新功能,更改的原因在 here 进行了解释。
【讨论】:
要实现这一点:
class Base {}
class Derived : Base {}
List<Derived> list = new List<Derived>();
IEnumerable<Base> sequence = list;
【讨论】: