泛型中的 <out T> 与 <T>
Posted
技术标签:
【中文标题】泛型中的 <out T> 与 <T>【英文标题】:<out T> vs <T> in Generics 【发布时间】:2012-06-08 23:05:42 【问题描述】:<out T>
和 <T>
有什么区别?例如:
public interface IExample<out T>
...
对比
public interface IExample<T>
...
【问题讨论】:
很好的例子是 IObservable泛型中的out
关键字用于表示接口中的类型T是协变的。详情请见Covariance and contravariance。
经典的例子是IEnumerable<out T>
。由于IEnumerable<out T>
是协变的,因此您可以执行以下操作:
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;
如果这不是协变的,上面的第二行将失败,即使在逻辑上它应该工作,因为 string 派生自 object。在将 variance in generic interfaces 添加到 C# 和 VB.NET(在 .NET 4 和 VS 2010 中)之前,这是一个编译时错误。
在 .NET 4 之后,IEnumerable<T>
被标记为协变,变成了IEnumerable<out T>
。由于IEnumerable<out T>
仅使用其中的元素,而从不添加/更改它们,因此将可枚举的字符串集合视为可枚举的对象集合是安全的,这意味着它是协变的。 p>
这不适用于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
也有一个选项 - 用于比较接口之类的东西。例如,IComparer<in T>
的工作方式正好相反。如果Bar
是Foo
的子类,您可以直接将具体的IComparer<Foo>
用作IComparer<Bar>
,因为IComparer<in T>
接口是逆变的。
【讨论】:
@ColeJohnson 因为Image
是一个抽象类;)您可以毫无问题地使用new List<object>() Image.FromFile("test.jpg") ;
,或者您也可以使用new List<object>() new Bitmap("test.jpg") ;
。你的问题是new Image()
是不允许的(你也不能var img = new Image();
)
泛型IList<object>
是一个奇怪的例子,如果你想要object
s,你就不需要泛型。
@ReedCopsey 您在评论中的回答不是自相矛盾吗?【参考方案2】:
为了容易记住 in
和 out
关键字的用法(也是协变和逆变),我们可以将继承想象成包装:
String : Object
Bar : Foo
【讨论】:
这不是错误的方式吗?逆变 = in = 允许使用较少派生类型代替更多派生类型。 / Covariance = out = 允许使用更多派生类型代替更少派生类型。就个人而言,看着你的图表,我读到的正好相反。 co u 变体(:对我来说【参考方案3】:考虑,
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<Banana>
,因为ICovariantSkinned<T>
是协变接口,而Banana
是Fruit
的类型,
接受ISkinned<Fruit>
的函数将只能接受ISkinned<Fruit>
。
【讨论】:
【参考方案4】:“out T
”表示T
类型是“协变的”。这限制了T
在泛型类、接口或方法的方法中仅作为返回(出站)值出现。这意味着您可以将类型/接口/方法转换为具有T
超类型的等价物。
例如。 ICovariant<out Dog>
可以转换为 ICovariant<Animal>
。
【讨论】:
我没有意识到out
强制 T
只能返回,直到我阅读了这个答案。整个概念现在更有意义了!【参考方案5】:
从您发布的链接中......
对于泛型类型参数,out 关键字指定类型 参数是协变的。
编辑: 同样,来自您发布的链接
有关详细信息,请参阅协变和逆变(C# 和 Visual Basic)。 http://msdn.microsoft.com/en-us/library/ee207183.aspx
【讨论】:
以上是关于泛型中的 <out T> 与 <T>的主要内容,如果未能解决你的问题,请参考以下文章