Проблема понимания ковариантной контравариантности с генериками в C # - PullRequest
0 голосов
/ 07 ноября 2018

Я не могу понять, почему следующий код C # не компилируется.

Как видите, у меня есть статический универсальный метод Something с параметром IEnumerable<T>T ограничен интерфейсом IA), и этот параметр не может быть неявно преобразован в IEnumerable<IA> .

Какое объяснение? (Я не ищу обходной путь, просто чтобы понять, почему он не работает).

public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }

public static class Test
{
    public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
    {
        var bar = foo.ToList();

        // All those calls are legal
        Something2(new List<IA>());
        Something2(new List<IB>());
        Something2(new List<CIA>());
        Something2(new List<CIAD>());
        Something2(new List<CIB>());
        Something2(new List<CIBD>());
        Something2(bar.Cast<IA>());

        // This call is illegal
        Something2(bar);

        return bar;
    }

    private static void Something2(IEnumerable<IA> foo)
    {
    }
}

Ошибка в строке Something2(bar):

Аргумент 1: невозможно преобразовать из 'System.Collections.Generic.List' в 'System.Collections.Generic.IEnumerable'

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

Я просто хотел дополнить отличный ответ Эрика инсайдером примером кода для тех, кто, возможно, не знаком с общими ограничениями.

Изменить подпись Something следующим образом: ограничение class должно стоять на первом месте .

public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA
0 голосов
/ 07 ноября 2018

Сообщение об ошибке недостаточно информативно, и это моя вина. Извините за это.

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

Возможно, вы сейчас говорите "но IA - это ссылочный тип". Да, это. Но вы не сказали, что T равно IA. Вы сказали, что T - это тип, который реализует IA, а тип значения может реализовывать интерфейс . Поэтому мы не знаем, будет ли работать ковариация, и мы запрещаем это.

Если вы хотите, чтобы ковариация работала, вы должны сообщить компилятору, что параметр типа является ссылочным типом с ограничением class, а также с ограничением интерфейса IA.

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

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