为什么像IEnumerable<out T>
类型T
这样的协变类型参数仅用于返回类型(只读)或反向逆变类型参数,如Action<in T>
类型T
仅用作参数类型(只写)?
换句话说,我认为纯协变概念与仅用于成员的返回类型的c#协变类型参数之间存在关系。
答案 0 :(得分:3)
为什么像
IEnumerable<out T>
这样的协变类型参数的类型T
仅用于返回类型?
首先关闭:T
不仅用于返回类型。例如:
interface I1<out T>{ T M1(); }
interface I2<out T>{ void M2(Action<I1<T>> a); }
在I1<T>
中,T
仅用于返回类型位置。但在I2
中,T
用于输入a
,而I1<T>
是Action
的输入,因此从某种意义上说它正在被使用在两个输入位置。
但是,让我们考虑一个更简单的案例。为什么我们可以I1
中的T
协变而不是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
返回什么,它始终为Mammal
,因此始终为Animal
。
但这不合法:
I1<Giraffe> i1g = i1m; // Not legal
Giraffe g = i1g.M1(); // Returns a tiger; assigned to giraffe, bad!
第一行必须是非法的,以便第二行永远不会执行。
现在你应该有足够的信息来弄清楚为什么逆变会以它的方式运作。记住,你总是可以回到一个简单的例子,问问自己&#34;如果这是合法的,我可以在以后犯下什么错误?&#34;类型系统正在保护您免于犯这些错误!
练习:对I2<T>
进行同样的分析。您是否明白为什么在两个输入位置使用T
是合法的,即使它是out
。 (提示:Action
是逆变,因此它会改变分配兼容性的方向。如果你反向指示两次会怎样?)
答案 1 :(得分:1)
所以我看到你的问题在哪里。答案应该是这样的。让我再次使用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;
你可以做这样的事情,因为string派生自object,所以只要你得到一个返回类型的方法派生自对象你就不会有问题。想象一下,你声明了相同的del,所以你知道你会得到一个对象,所以你声明一个变量:
object returnedType = del2();
你不必关心del2是返回int还是字符串,因为它们是从对象派生的,它与以下内容相似:
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
它完全是关于纯多态性的。在协方差中,我们除了一个普通类型,但如果我们得到更具体的类型,它对我们没有任何改变。 相反,我们知道我们将传递什么,但是如果我们将字符串分配给字符串或字符串到对象并不重要。 (但我们不能将对象分配给字符串)。