猿问

为什么协变类型参数仅用于成员的返回类型?

为什么像IEnumerable<out T>type这样的协变类型参数T只用于返回类型(只读)或像Action<in T>type这样的逆逆变类型参数T只用作参数类型(只写)?换句话说,我认为纯协变概念和仅用于成员返回类型的 c# 协变类型参数之间存在关系。


慕标5832272
浏览 177回答 2
2回答

互换的青春

为什么像IEnumerable<out T>type这样的协变类型参数T只用于返回类型?第一关:T不具备返回类型仅被使用。例如:interface I1<out T>{ T M1(); }interface I2<out T>{ void M2(Action<I1<T>> a); }在I1<T>,T仅用于返回类型的位置。但是 in I2,T用在一个 input 中a, anI1<T>是 the 的输入Action,所以从某种意义上说,它在这里被用在两个输入位置。但让我们考虑一个更简单的情况。为什么我们可以使I1in 协变T但不能使in逆变T?原因是因为协方差是安全的,而逆变不是。我们可以看到协方差是安全的:class Animal {}class Mammal : Animal {}class Tiger : Mammal {}class Giraffe : Mammal {}class C : I1<Mammal> {&nbsp; &nbsp; public Mammal M1() { return new Tiger(); }}I1<Mammal> i1m = new C(); // LegalI1<Animal> i1a = i1m; // LegalAnimal a = i1a.M1(); // Returns a tiger; assigned to animal, good!无论C.M1返回什么,它总是 a Mammal,因此总是 an Animal。但这不可能是合法的:I1<Giraffe> i1g = i1m; // Not legalGiraffe g = i1g.M1(); // Returns a tiger; assigned to giraffe, bad!第一行必须是非法的,以便第二行永远不会执行。现在你应该有足够的信息来弄清楚为什么逆变是这样工作的。记住,你总是可以回到一个简单的例子并问自己“如果这是合法的,我以后会犯什么错误?” 类型系统正在保护您免于犯这些错误!练习:对I2<T>. 你明白为什么T在两个输入位置使用是合法的,即使它是out. (提示:Action是逆变的,所以它反转了赋值兼容性的方向。如果你反转方向两次会发生什么?)

森林海

所以我知道你的问题在哪里。答案应该是这样。让我再次使用MSDN 中的示例:static object GetObject() { return null; }&nbsp;&nbsp;static void SetObject(object obj) { }&nbsp;&nbsp;static string GetString() { return ""; }&nbsp;&nbsp;static void SetString(string str) { }&nbsp;&nbsp;static void Test()&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp; &nbsp; // Covariance. A delegate specifies a return type as object,&nbsp;&nbsp;&nbsp; &nbsp; // but you can assign a method that returns a string.&nbsp;&nbsp;&nbsp; &nbsp; Func<object> del = GetString;&nbsp;&nbsp;&nbsp; &nbsp; // Contravariance. A delegate specifies a parameter type as string,&nbsp;&nbsp;&nbsp; &nbsp; // but you can assign a method that takes an object.&nbsp;&nbsp;&nbsp; &nbsp; Action<string> del2 = SetObject;&nbsp; &nbsp; //However you can't use del2 this way, after this assingment:&nbsp; &nbsp; //del2(new object);}&nbsp;这很难理解,对我来说它是安静的高级抽象。协方差让我们仔细看看Func<object> del = GetString; 你被允许做这样的事情,因为字符串派生自对象,所以只要你得到一个返回类型派生自对象的方法,你就没有问题。想象一下,您声明了相同的 del,因此您所知道的将获得对象,因此您声明了一个变量:object returnedType = del2();您不必关心 del2 是返回 int 还是 string,因为它们派生自 object 它将类似于:&nbsp;object returnedType = "string"; //Here we know what is on the left side&nbsp;//If we assign to del2 method with return type string.逆变现在让我们看看Action<string> del2 = SetObject; 现在你假设你会得到一个字符串来方法,所以如果有人,有一天会使用你的委托和方法,就像SetObject(object obj)之前一样:object obj= "string"; //Here we know what is on the right假设这都是关于纯多态性的。在协方差中,我们排除了一种通用类型,但如果我们获得更具体的类型,它不会对我们产生任何改变。逆变我们知道我们将传递什么,但我们是否将字符串分配给字符串或字符串分配给对象并不重要。(但我们不能将对象分配给字符串)。
随时随地看视频慕课网APP
我要回答