泛型中的<out T> vs <T>

Col*_*son 168 c# generics covariance

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

public interface IExample<out T>
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

public interface IExample<T>
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

Ree*_*sey 198

out泛型中的关键字用于表示接口中的类型T是协变的.有关详细信息,请参阅协方差和逆变.

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

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;
Run Code Online (Sandbox Code Playgroud)

如果这不是协变的话,上面的第二行将失败,即使逻辑上它应该起作用,因为字符串派生自对象.之前在通用接口方差加入到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
Run Code Online (Sandbox Code Playgroud)

然后你可以打电话:

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

当然,这会失败 - 所以IList<T>不能标记为协变.

btw还有一个选项in- 比较接口之类的东西. IComparer<in T>例如,工作方式相反.你可以IComparer<Foo>直接使用IComparer<Bar>if 作为if Bar的子类Foo,因为IComparer<in T>接口是逆变的.

  • @ReedCopsey你是不是在评论中反驳了自己的答案? (5认同)
  • @ColeJohnson因为`Image`是一个抽象类;)你可以做`new List <object>(){Image.FromFile("test.jpg")};`没有问题,或者你可以做`new List <object >(){new Bitmap("test.jpg")};`.你的问题是不允许`new Image()`(你不能做`var img = new Image();`或者) (4认同)
  • 一个通用的`IList <object>`是一个奇怪的例子,如果你想要`object`s你不需要泛型. (4认同)

o0o*_*o0o 59

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

String : Object
Bar : Foo
Run Code Online (Sandbox Code Playgroud)

进出

  • 这不是错误的方法吗?Contravariance = in =允许使用较少的派生类型来代替更多派生类型./ Covariance = out =允许使用更多派生类型代替较少派生的类型.就个人而言,看看你的图表,我把它读作与之相反. (11认同)

Jod*_*ell 48

考虑,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}
Run Code Online (Sandbox Code Playgroud)

和功能,

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }
Run Code Online (Sandbox Code Playgroud)

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

接受的功能ISkinned<Fruit>只能接受ISkinned<Fruit>.


Jam*_*rld 32

" out T"表示类型T是"协变".这限制T为仅作为泛型类,接口或方法的方法中的返回(出站)值出现.这意味着您可以将类型/接口/方法强制转换为具有超类型的等效项T.
例如ICovariant<out Dog>可以演员ICovariant<Animal>.

  • 我没有意识到`out`强制只能返回`T`,直到我读到这个答案.现在整个概念更有意义! (8认同)

Bra*_*ham 6

从您发布的链接....

对于泛型类型参数,out关键字指定type参数是协变的.

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

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