почему параметры ковариантного типа используются только для возвращаемых типов членов? - PullRequest
0 голосов
/ 12 июня 2018

Почему параметры ковариантного типа, такие как IEnumerable<out T> type T, используются только для типа возвращаемого значения (только для чтения) или обратного параметра контравариантного типа, такого как Action<in T> type T, используемого только как тип параметра (только для записи)?другими словами, я думаю, что существует связь между чисто ковариантным понятием и параметрами c # ковариантного типа, используемыми только для возвращаемых типов членов.

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

Почему параметры ковариантного типа, такие как 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.

Но это не может бытьlegal:

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

Первая строка должна быть недопустимой, чтобы вторая строка никогда не выполнялась.

Теперь у вас должно быть достаточно информации, чтобы выяснить, почему контравариантность работает так, как она работает.Помните, что вы всегда можете вернуться к простому примеру и спросить себя: «Если это было законно, какие ошибки я мог бы сделать позже?»Система типов защищает вас от этих ошибок!

Упражнение: Сделайте такой же анализ I2<T>.Вы понимаете, почему разрешено использовать T в двух позициях ввода, даже если это out.(Подсказка: Action является контравариантным , поэтому он меняет направление совместимости назначений. Что произойдет, если вы измените направление дважды ?)

0 голосов
/ 12 июня 2018

Так что я вижу, где твоя проблема.Ответ должен быть таким.Позвольте мне снова использовать пример из 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 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

Предполагая, что

, это все о чистом полиморфизме.В ковариации мы исключаем один общий тип, но он ничего не меняет для нас, если мы получаем более конкретный тип.В отличие от этого мы знаем, что мы будем передавать, но не имеет значения, будем ли мы назначать строку для строки или строку для объекта.(но мы не можем присвоить объект строке).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...