Вопрос:

Почему ковариантные неявные приведения игнорируют общие ограничения?

c# .net

138 просмотра

1 ответ

3934 Репутация автора

static IEnumerable<U> DoSomething<T, U>(IEnumerable<T> a)
    where T : U
{
    // Works, compiler can compile-time statically cast
    // T as U.
    T testA = default(T);
    U testB = testA;

    // And the following works, though:
    IEnumerable<string> test2A = null;
    IEnumerable<object> test2B = test2A;

    // Doesn’t work. Compiler cannot compile-time statically
    // cast IEnumerable<T> to IEnumerable<U> even though it is
    // out and T is U.
    return a;
}

У меня есть код, где возможность выполнения этого типа неявного преобразования спасет меня от написания большого количества стандартного кода реализации интерфейса. Похоже, это та вещь, с которой ковариация должна была помочь. Но я всегда получаю эту ошибку в return a;строке выше:

ошибка CS0266: невозможно неявное преобразование типа 'System.Collections.Generic.IEnumerable ' в 'System.Collections.Generic.IEnumerable '. Существует явное преобразование (вам не хватает приведения?)

Почему это так, и есть ли способ обойти это, не делая ничего подобного return from o in a select o;?

Автор: binki Источник Размещён: 22.08.2016 09:21

Ответы (1)


6 плюса

3934 Репутация автора

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

static IEnumerable<U> DoSomethingElse<T, U>(IEnumerable<T> a)
    where T : class, U
{
    // Works! Ridik!
    return a;
}

А также, что следующее не с тем же сообщением об ошибке:

static void Blah()
{
    // Fails for I bet the same reason that DoSomething{T, U} fails:
    IEnumerable<int> a = null;
    IEnumerable<object> b = a;
}

ошибка CS0266: невозможно неявно преобразовать тип 'System.Collections.Generic.IEnumerable ' в 'System.Collections.Generic.IEnumerable '. Существует явное преобразование (вам не хватает приведения?)

Так что это, похоже, связано с тем, как .net ограничивает определенные типы назначений ссылочными типами, потому что в этих ситуациях боксирование может быть неправильным (например, вы можете использовать ссылочные типы и фактически работать с копией типа значения) или очень трудно / невозможно реализовать во время выполнения (учитывая, что IEnumerable<int>вам нужно реализовать адаптирующий класс для переноса. ОК, так что это звучит так, как .net не может / не должен пытаться сделать для вас во время выполнения). Я рассматриваю это как ситуацию, в которой .net допускает полиморфизм в стиле указателя, который по своей природе несовместим с концепцией типов значений.

Поэтому для моего случая мне не нужно поддерживать типы значений в моем API, и добавление classограничения делает все волшебным!

Автор: binki Размещён: 22.08.2016 09:21 32x32