在Generics中<out t =“”> vs <t> </t> </out>

时间:2012-06-08 23:05:43

标签: c# generics covariance

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

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

VS

public interface IExample<T>
{
    ...
}

5 个答案:

答案 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)

为了便于记住inout关键字(也是协方差和逆变)的使用,我们可以将继承映像为包装:

String : Object
Bar : Foo

in/out

答案 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>是协变接口而BananaFruit的类型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