简体   繁体   English

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

[英]why covariant type parameters are used only for the return types of the members?

Why covariant type paramaters like IEnumerable<out T> type T used only for the return type (read-only) or inverse contravariant type parameter like Action<in T> type T used only as parameter type (write-only)? 为什么像IEnumerable<out T>类型T这样的协变量类型参数仅用于返回类型(只读),或者像诸如Action<in T>类型T反协变量类型参数仅用作参数类型(只写)? in other word i think there exist a relation between pure covariant concept and c# covariant type paramaters used only for the return types of the members. 换句话说,我认为纯协变量概念和仅用于成员返回类型的c#协变量类型参数之间存在关系。

Why do covariant type parameters like IEnumerable<out T> have type T used only for the return type? 为什么像IEnumerable<out T>这样的协变类型参数将类型T仅用于返回类型?

First off: T does not have to be used only for the return type. 第一关: T 没有返回类型仅被使用。 For example: 例如:

interface I1<out T>{ T M1(); }
interface I2<out T>{ void M2(Action<I1<T>> a); }

In I1<T> , T is used only in return type positions. I1<T>T仅用于返回类型位置。 But in I2 , T is used in an input a , and an I1<T> is the input of the Action , so in a sense it is being used in two input positions here. 但是在I2T用于输入a ,而I1<T>Action的输入,因此在某种意义上,它在此处的两个输入位置使用。

But let's consider just a simpler case. 但是,让我们考虑一个更简单的情况。 Why can we make I1 covariant in T but not contravariant in T ? 为什么我们可以I1协在T ,但不是逆变T

The reason is because covariance is safe and contravariance is not. 原因是因为协方差是安全的,而协方差则不是。 We can see that covariance is safe: 我们可以看到协方差是安全的:

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!

No matter what C.M1 returns, it is always a Mammal and therefore always an Animal . 无论C.M1返回什么,它始终是Mammal ,因此始终是Animal

But this cannot be legal: 但这不合法:

I1<Giraffe> i1g = i1m; // Not legal
Giraffe g = i1g.M1(); // Returns a tiger; assigned to giraffe, bad!

The first line has to be illegal so that the second line never executes. 第一行必须是非法的,这样第二行才能执行。

Now you should have enough information to figure out why contravariance works the way it does. 现在,您应该有足够的信息来找出为什么逆方差会如此起作用。 Remember, you can always come back to a simple example and ask yourself "if this was legal, what mistakes could I make later?" 记住,您总是可以回到一个简单的示例,然后问自己:“如果这样做合法,以后我会犯什么错误?” The type system is protecting you from making those mistakes! 类型系统可以防止您犯这些错误!

Exercise: Do this same analysis of I2<T> . 练习:对I2<T>相同的分析。 Do you see why it is legal to use T in two input positions even though it is out . 你知道为什么即使在out两个输入位置使用T合法吗? (Hint: Action is contravariant , so it reverses the direction of assignment compatibility. What happens if you reverse directions twice ?) (提示: Action互变的 ,因此它会反转分配兼容性的方向。如果两次反转方向会怎样?)

So i see where is your problem. 所以我知道你的问题在哪里。 Answer should be this way. 答案应该是这样。 Let me again use example from MSDN : 让我再次使用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);
} 

This is hard to understand and for me it is quiet high level of abstraction. 这很难理解,对我来说,这是安静的高级抽象。

Covariance 协方差

Let's take a closer look to Func<object> del = GetString; 让我们仔细看看Func<object> del = GetString; You are allowed to do such thing because string derives from object, so as long as you will get a method with return type derives from object you don't have problem with it. 您可以这样做,因为字符串是从对象派生的,因此只要您获得的返回类型是从对象派生的方法,就不会遇到问题。 Imagine you declare the same del so all you know you will get object so you declare a variable: 想象一下,您声明了相同的del,因此您所知道的所有对象都将成为对象,因此您声明了一个变量:

object returnedType = del2();

You don't have to care about whether del2 return int or string because they derives from object it will be similar with: 您不必担心del2是否返回i​​nt或string,因为它们是从对象派生的,它类似于:

 object returnedType = "string"; //Here we know what is on the left side
 //If we assign to del2 method with return type string.

Contravariance 逆变

Now let's take a look to Action<string> del2 = SetObject; 现在让我们看一下Action<string> del2 = SetObject; Now you assume that you will get a string to method so if somebody, someday will use your delegate with method like SetObject(object obj) so it will be the same as earlier: 现在,您假定将获得方法的字符串,因此,如果有人使用,则某天将您的委托与SetObject(object obj)类的方法一起使用,因此它将与之前的方法相同:

object obj= "string"; //Here we know what is on the right

Assuming 假设

it's all about pure polymorphism. 都是关于纯多态性的。 In covariance, we except one general type but it changes nothing for us if we get more specific type. 在协方差中,除了一个通用类型外,如果我们得到更具体的类型,它对我们没有任何改变。 In contravariance we know what we will passing, but it doesn't matter if we will assign string to string or string to object. 相反,我们知道要传递的内容,但是将字符串分配给字符串还是将字符串分配给对象都没有关系。 (but we can't assign object to string). (但是我们不能将对象分配给字符串)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM