2 回答
TA贡献1797条经验 获得超6个赞
为什么像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> {
public Mammal M1() { return new Tiger(); }
}
I1<Mammal> i1m = new C(); // Legal
I1<Animal> i1a = i1m; // Legal
Animal a = i1a.M1(); // Returns a tiger; assigned to animal, good!
无论C.M1返回什么,它总是 a Mammal,因此总是 an Animal。
但这不可能是合法的:
I1<Giraffe> i1g = i1m; // Not legal
Giraffe g = i1g.M1(); // Returns a tiger; assigned to giraffe, bad!
第一行必须是非法的,以便第二行永远不会执行。
现在你应该有足够的信息来弄清楚为什么逆变是这样工作的。记住,你总是可以回到一个简单的例子并问自己“如果这是合法的,我以后会犯什么错误?” 类型系统正在保护您免于犯这些错误!
练习:对I2<T>. 你明白为什么T在两个输入位置使用是合法的,即使它是out. (提示:Action是逆变的,所以它反转了赋值兼容性的方向。如果你反转方向两次会发生什么?)
TA贡献2011条经验 获得超2个赞
所以我知道你的问题在哪里。答案应该是这样。让我再次使用MSDN 中的示例:
static object GetObject() { return null; }
static void SetObject(object obj) { }
static string GetString() { return ""; }
static void SetString(string str) { }
static void Test()
{
// Covariance. A delegate specifies a return type as object,
// but you can assign a method that returns a string.
Func<object> del = GetString;
// Contravariance. A delegate specifies a parameter type as string,
// but you can assign a method that takes an object.
Action<string> del2 = SetObject;
//However you can't use del2 this way, after this assingment:
//del2(new object);
}
这很难理解,对我来说它是安静的高级抽象。
协方差
让我们仔细看看Func<object> del = GetString; 你被允许做这样的事情,因为字符串派生自对象,所以只要你得到一个返回类型派生自对象的方法,你就没有问题。想象一下,您声明了相同的 del,因此您所知道的将获得对象,因此您声明了一个变量:
object returnedType = del2();
您不必关心 del2 是返回 int 还是 string,因为它们派生自 object 它将类似于:
object returnedType = "string"; //Here we know what is on the left side
//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
假设
这都是关于纯多态性的。在协方差中,我们排除了一种通用类型,但如果我们获得更具体的类型,它不会对我们产生任何改变。逆变我们知道我们将传递什么,但我们是否将字符串分配给字符串或字符串分配给对象并不重要。(但我们不能将对象分配给字符串)。
- 2 回答
- 0 关注
- 176 浏览
添加回答
举报