generics中的<out T> vs <T>

<out T><T>什么区别? 例如:

 public interface IExample<out T> { ... } 

 public interface IExample<T> { ... } 

我从MSDN得到的唯一信息是

您可以在generics接口和委托中使用out关键字。

generics中的out关键字用于表示接口中的typesT是协变的。 有关详细信息,请参阅协变和逆变 。

经典的例子是IEnumerable<out T> 。 由于IEnumerable<out T>是协变的,因此您可以执行以下操作:

 IEnumerable<string> strings = new List<string>(); IEnumerable<object> objects = strings; 

上面的第二行将失败,如果这不是协变,即使逻辑上它应该工作,因为string派生自对象。 在通用接口的差异被添加到C#和VB.NET(在与VS 2010的.NET 4中)之前,这是一个编译时错误。

在.NET 4之后, IEnumerable<T>被标记为协变,并成为IEnumerable<out T> 。 由于IEnumerable<out T>仅使用其中的元素,并且从不添加/更改它们,所以将string的可枚举集合视为可枚举的对象集合是安全的,这意味着它是协变的

这不适用于IList<T>types,因为IList<T>具有Add方法。 假设这将被允许:

 IList<string> strings = new List<string>(); IList<object> objects = strings; // NOTE: Fails at compile time 

你可以打电话给:

 objects.Add(new Image()); // This should work, since IList<object> should let us add **any** object 

这当然会失败 – 所以IList<T>不能被标记为协变。

也有,顺便说一句,这是用于比较接口的东西。 例如, IComparer<in T>工作方式相反。 如果BarFoo的子类,则可以直接使用具体的IComparer<Foo>作为IComparer<Bar> ,因为IComparer<in T>接口是逆变的

为了容易地记住out关键字的使用(也就是协变和逆变),我们可以将inheritance映像为包装:

 String : Object Bar : Foo 

进出

考虑,

 class Fruit {} class Banana : Fruit {} interface ICovariantSkinned<out T> {} interface ISkinned<T> {} 

和function,

 void Peel(ISkinned<Fruit> skinned) { } void Peel(ICovariantSkinned<Fruit> skinned) { } 

接受ICovariantSkinned<Fruit>的函数将能够接受ICovariantSkinned<Fruit>ICovariantSkinned<Bananna>因为ICovariantSkinned<T>是一个协变接口,而Banana是一种Fruit

接受ISkinned<Fruit>的函数将只能接受ISkinned<Fruit>

out T ”意味着typesT是“协变”的。 这限制了T只出现在generics类,接口或方法的方法中的返回(出站)值。 这意味着你可以将types/接口/方法强制转换为超types的T
例如ICovariant<out Dog>可以投射到ICovariant<Animal>

从你发布的链接

对于genericstypes参数, out关键字指定types参数是协变的

编辑 :再次,从您发布的链接

有关更多信息,请参见协变和逆变(C#和Visual Basic)。 http://msdn.microsoft.com/en-us/library/ee207183.aspx