Как стать автором
Обновить
21
0
Никита @Accetone

Пользователь

Отправить сообщение
Извините, насчёт обобщённых интерфейсов перепутал.

Обобщённые интерфейсы ковариантны по выходному значению и котнравариантны по входным аргументам / 4.0 и выше

interface IMagic<in TIn, out TOut>
{
    TOut DoMagic(TIn smth);
}

class Magic : IMagic<Fruit, Orange>
{
    public Orange DoMagic(Fruit smth)
    {
        return new Orange();
    }
}
    
class Program
{
    static void Main(string[] args)
    {
        // это обычно т.к. Magic реализует интерфейс IMagic<Fruit, Orange>
        IMagic<Fruit, Orange> i1 = new Magic();

        // используется и ковариантность и контравариантность т.к. записываем
        // IMagic<Fruit, Orange> в IMagic<Orange, Fruit>
        IMagic<Orange, Fruit> i2 = new Magic();
    }
}
Если я правильно понял, то:

public interface ISolver<in TInputData, out TAnswerData>
{
    TAnswerData Solve(TInputData data);
}
С# / .Net Framework

Массивы ковариантны / 2.0 и выше
Fruit[] fruits = new Orange[10];

Обобщённые коллекции инвариантны
List<Fruit> fruits = new List<Orange>(); // ошибка времени компиляции

Обобщённые интерфейсы ковариантны / 4.0 и выше
IEnumerable<Orange> oranges = new List<Orange>();
IEnumerable<Fruit> fruits = oranges;

Группа методов ковариантна по выходному значению и котнравариантна по входным аргументам / 2.0 и выше
// творим из фрукта апельсин ;) 
static Orange Upgrade(Fruit fruit) { return new Orange(); }

// обобщённый тип делегата для указания на методы 
// принимающие TIn и возвращающие TOut
delegate TOut Func<TIn, TOut>(TIn smth);

static void Main(string[] args)
{
    // это обычно т.к. Upgrade принимает Fruit и возвращает Orange
    // а делегат как раз указывает на такие функции
    Func<Fruit, Orange> action = Upgrade;

    // используется и ковариантность и контравариантность т.к.
    // Upgrade принимает Fruit и возвращает Orange
    // а делегат указывает на функции принимающие Orange и возвращающие Fruit 
    Func<Orange, Fruit> action2 = Upgrade;
}

Объекты делегатов ковариантны по выходному значению и котнравариантны по входным аргументам / 4.0 и выше
Func<Fruit, Orange> action = fruit => new Orange();
Func<Orange, Fruit> action2 = action;

Приведённый мной код (комментарий с дополнительным примером) компилируется начиная с .Net Framework 4.0 и выше.
Да, в теории всё так, как вы поняли. Но правильнее будет сказать, что компиляторы не реализуют возможности ковариантности и контравариантности, вместо накладывания ограничений.

А насчёт контравариантности аргументов методов в С#, можете привести краткий пример того как у вас работает и как вы хотите что бы работало?
Здесь речь идёт не о инвариантности для обеспечения типобезопасности. Говорится, что, если поддерживается ковариантность или контравариантность, тех же коллекций, то компилятор, как правило, не проверяет корректно ли вы ей пользуетесь. Так компилятор может пропустить (не выдать ошибку времени компиляции) этот кусок кода:

List<Device> devices = new List<Keyboard>();
devices.Add(new Device()); // ошибка времени выполнения

Это, вроде как очевидно, но я решил всё таки упомянуть.

Спасибо за комментарий.
В разделе «Классическая ковариантность», вроде, неплохой пример. В любом случае, можно рассмотреть ещё один.

class Fruit
{
    public void Eat()
    {
        Console.WriteLine("You ate fruit!");
    }
}

class Orange : Fruit {}

class Program
{
    static void Main(string[] args)
    {
        // создаём объект делегата, который принимает фрукт и возвращает void
        Action<Fruit> actionWithFruit = fruit => fruit.Eat();

        // создаём список из трёх апельсинов
        List<Orange> oranges = new List<Orange> { new Orange(), new Orange(), new Orange() };

        // по факту мы должны передать в метод ForEach объект делгата Action<Orange>,
        // который принимает апельсин и возвращает void, но благодаря контравариантности 
        // можем передать Action<Fruit> т.е. записываем в переменную типа Action<Orange> объект типа Action<Fruit>
        // контравариантность переварачивает порядок наследования
        // Orange : Fruit => Action<Fruit> : Action<Orange>
        // Fruit fruit = new Orange();
        // Action<Orange> action = new Action<Fruit>(fruit => fruit.Eat());
        oranges.ForEach(actionWithFruit);
    }
}


Скажите, если нужно пояснить код или что-то осталось неясным, буду рад помочь.

Информация

В рейтинге
Не участвует
Откуда
Минск, Минская обл., Беларусь
Дата рождения
Зарегистрирован
Активность