Ошибка ковариации на интерфейсе типа Valued - PullRequest
1 голос
/ 21 февраля 2020

У меня есть универсальный c интерфейс, содержащий ковариантный параметр TValue и абстрактный класс, который выполняет некоторые повторяющиеся действия, чтобы освободить дочерние классы от этого бремени. Затем у меня есть 2 подкласса, которые расширяются от этого абстрактного, первый задает параметр generi c как строку, а второй - как int.

Это часть кода, взятая из проекта, чрезмерно упрощенная просто чтобы сосредоточиться на этом вопросе.

public interface IElement<out TValue>
{
    string Name { get; }
    TValue Value { get; }
}

public abstract class Element<TValue> : IElement<TValue>
{
    public string Name { get; }
    public TValue Value { get; set; }

    public Element(string name)
    {
        Name = name;
    }
}

public class Name : Element<string>
{
    public Name() : base("Name") { }
}

public class Height : Element<int>
{
    public Height() : base("Height") { }
}

В основном - и это не то, что я делаю в своем коде, но довольно просто иллюстрирует проблему, с которой я сталкиваюсь - если я пытаюсь назначить Имя для Элемент IElement, удерживающий объект, подобный следующему:

IElement<object> element = new Name();

Это успешно, как я и ожидал, поскольку параметр TValue в IElement является ковариантным. Однако, если я установлю его на Height:

IElement<object> element = new Height();

, я получу ошибку Cannot implicitly convert type 'Height' to 'IElement<object>'. An explicit conversion exists (are you missing a cast?).

Теперь я не знаю, почему это работает с классом, который устанавливает generi c параметр в виде строки, но не с целым числом (или перечислением, как у меня также есть в проекте некоторые перечисления). Это потому, что string - это класс, а int - это структура?

Любая помощь приветствуется.

1 Ответ

1 голос
/ 21 февраля 2020

Просто, потому что один является типом значения . CLR запрещает его, так как ему необходимо сохранить свою идентичность, в то время как бокс - нет.

Eri c Lippert имеет отличный блог об этом на Представительство и личность

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

Кроме того, вы можете прочитать намного больше об идентичности, преобразовании, Обобщения и дисперсия в спецификациях в различных местах

11.2.11 Неявные преобразования, включающие параметры типа

Для параметра типа T, который, как известно, не является ссылочный тип (§15.2.5), следующие преобразования, включающие T, считаются преобразованиями бокса (11.2.8) во время компиляции. Во время выполнения, если T является типом значения, преобразование выполняется как преобразование бокса. Во время выполнения, если T является ссылочным типом, преобразование выполняется как неявное ссылочное преобразование или преобразование идентификатора.

...