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>
工作方式相反。 如果Bar
是Foo
的子类,则可以直接使用具体的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