<out T>
和<T>
之间有什么区别?例如:
public interface IExample<out T>
{
...
}
VS
public interface IExample<T>
{
...
}
答案 0 :(得分:187)
泛型中的out
关键字用于表示接口中的类型T是协变的。有关详细信息,请参阅Covariance and contravariance。
经典的例子是IEnumerable<out T>
。由于IEnumerable<out T>
是协变的,因此您可以执行以下操作:
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;
如果这不是协变的话,上面的第二行会失败,即使逻辑上它应该有效,因为字符串派生自对象。在将variance in generic interfaces添加到C#和VB.NET(使用VS 2010的.NET 4中)之前,这是一个编译时错误。
在.NET 4之后,IEnumerable<T>
被标记为协变,并成为IEnumerable<out T>
。由于IEnumerable<out T>
仅使用其中的元素,并且从不添加/更改它们,因此将可枚举的字符串集合视为可枚举的对象集合是安全的,这意味着它是协变。
这不适用于类似IList<T>
的类型,因为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>
不能被标记为协变。
btw还有in
的选项 - 比较接口之类的东西。例如,IComparer<in T>
以相反的方式运作。如果IComparer<Foo>
是IComparer<Bar>
的子类,则可以直接使用具体Bar
作为Foo
,因为IComparer<in T>
接口是逆变
答案 1 :(得分:48)
为了便于记住in
和out
关键字(也是协方差和逆变)的使用,我们可以将继承映像为包装:
String : Object
Bar : Foo
答案 2 :(得分:40)
考虑,
class Fruit {}
class Banana : Fruit {}
interface ICovariantSkinned<out T> {}
interface ISkinned<T> {}
和功能,
void Peel(ISkinned<Fruit> skinned) { }
void Peel(ICovariantSkinned<Fruit> skinned) { }
接受ICovariantSkinned<Fruit>
的功能将能够接受ICovariantSkinned<Fruit>
或ICovariantSkinned<Bananna>
,因为ICovariantSkinned<T>
是协变接口而Banana
是Fruit
的类型1}},
接受ISkinned<Fruit>
的功能只能接受ISkinned<Fruit>
。
答案 3 :(得分:28)
“out T
”表示类型T
是“协变”。这限制了T
仅作为泛型类,接口或方法的方法中的返回(出站)值出现。这意味着您可以将类型/接口/方法强制转换为具有超级类型T
的等效项。
例如。 ICovariant<out Dog>
可以投放到ICovariant<Animal>
。
答案 4 :(得分:5)
从您发布的链接....
对于泛型类型参数, out关键字指定该类型 参数是协变的。
修改强>: 再次,从您发布的链接
有关更多信息,请参阅协方差和逆变(C#和Visual Basic)。 http://msdn.microsoft.com/en-us/library/ee207183.aspx