<out T>和<T>之间的区别是什么?例如:

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

vs.

public interface IExample<T>
{
    ...
}

当前回答

我认为VS2022的这张截图是非常描述性的——它说明了这对泛型施加了什么样的约束:

其他回答

“out T”意味着T型是“协变的”。这限制了T仅作为泛型类、接口或方法的方法中的返回(出站)值出现。这意味着您可以将类型/接口/方法转换为具有T超类型的等效类型。 例:ICovariant<out Dog>可以施放到ICovariant<Animal>。

泛型中的out关键字用于表示接口中的类型T是协变的。详见协方差和逆变。

经典的例子是IEnumerable<out T>。因为IEnumerable<out T>是协变的,你可以这样做:

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

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

在。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>不能被标记为协变。

顺便说一下,还有一个in -选项,它被比较接口之类的东西使用。例如,T>中的IComparer<以相反的方式工作。如果Bar是Foo的子类,你可以直接使用具体的IComparer<Foo>作为IComparer<Bar>,因为IComparer<在T>接口中是逆变的。

考虑,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}

函数,

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }

接受icovariant蒙皮<Fruit>的函数将能够接受icovariant蒙皮<Fruit>或icovariant蒙皮<Banana>,因为icovariant蒙皮<T>是一个协变接口,香蕉是一种水果,

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

为了方便地记住in和out关键字(以及协方差和逆变)的用法,我们可以将继承图像包装为:

String : Object
Bar : Foo

我在这篇文章中找到的最简单的解释是

T> < -中的interface ISomeName<表示T只能作为a传递 方法的参数(它进入接口的方法,就这样 在里面,也许这就是为什么我们使用here关键字“in” 只是巧合吗?) interface ISomeName<out T> < -表示T只能返回为 方法结果(它是我们从方法中接收到的结果,因此它会输出 它-哇,再次听起来是正确的!)