2012-06-08 41 views
121

<out T><T>有什么区别?例如:<out T> vs <T> in Generics

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

public interface IExample<T> 
{ 
    ... 
} 

我从MSDN得到的唯一信息是,

可以使用了关键字通用接口和委托。

+1

很好的例子是的IObservable 和IObserver ,定义在mscorlib中的系统ns中。公共接口IObservable 和公共接口IObserver 。同样,IEnumerator的,IEnumerable的 VivekDev

回答

158

泛型中的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>不能标记协变。

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

+3

@ColeJohnson因为'Image'是一个抽象类)你可以做'新名单(){Image.FromFile( “test.jpg放在”)};'没有问题,或者您也可以做'新列表(){新的位图(“test.jpg”)};'以及。与你的问题是,'新画面()'是不允许的(你不能做'变种IMG =新的图片();'要么) –

+3

通用IList的''是一个奇怪的例子,如果你想'对象'你不需要泛型。 – Jodrell

+3

@ReedCopsey你在评论中是否与自己的回答相矛盾? – MarioDS

5

从您发布的链接....

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

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

欲了解更多信息,请参见协变和逆变(C#和Visual Basic)。 http://msdn.microsoft.com/en-us/library/ee207183.aspx

23

out T” 是指式T是 “协变”。这限制了T仅出现在泛型类,接口或方法的方法中的返回(出站)值。其含义是您可以将类型/接口/方法强制转换为超类型T的等效项。
例如ICovariant<out Dog>可以投射到ICovariant<Animal>

+4

我没有意识到'out'强制''T'只能返回,直到我读到这个答案。整个概念现在更有意义! – MarioDS

35

考虑,

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

接受功能s ISkinned<Fruit>将只能接受ISkinned<Fruit>

36

对于记忆的in容易使用和out关键字(也协方差和逆变),我们可以图像继承作为包装:

String : Object 
Bar : Foo 

in/out

+0

这使得它很清楚。 – antiduh

+4

这不是错误的方法吗? Contravariance = in =允许使用更少的派生类型来代替更多的派生类型。 /Covariance = out =允许使用更多派生类型来代替派生。 个人,看着你的图,我把它读作的是相反的。 –