Вопрос по generics, c# – Не удается преобразовать из универсального типа в интерфейс

7

Я получаю сообщение об ошибке при попытке добавить универсальный объект в список & lt; & gt ;.

Вероятно, это связано с ковариацией и контравариантностью, но я не уверен, как обойти это. Я пытался ограничить свои общие типы, используяwhere T: IRegister.

У меня есть интерфейс для представления регистра, а затем два класса, которые представляют ByteRegister и DoubleWordRegister.

public interface IRegister
{
    string Name {get;set;}
}

 public class ByteRegister : IRegister
{
...
}

public class DoubleWordRegister : IRegister
{
...
}

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

public class RegisterBlock<T> where T:IRegister
{
   private IList<T> _registers;

 ... constructors, properties etc

    public void AddRegister(T register)
    {
        _registers.Add(register);
    }
}

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

public class RegisterMap
{
    private List<RegisterBlock<IRegister>> _blocks;

    public RegisterMap()
    {
        _blocks = new List<RegisterBlock<IRegister>>();

        RegisterBlock<ByteRegister> block1= new RegisterBlock<ByteRegister>("Block1", 0);
        block1.AddRegister(new ByteRegister("Reg1"));
        block1.AddRegister(new ByteRegister("Reg2"));
        _blocks.Add(block1);

        RegisterBlock<DoubleWordRegister> block2= new RegisterBlock<DoubleWordRegister>("Block2", 10);
        block2.AddRegister(new DoubleWordRegister("Reg3"));
        block2.AddRegister(new DoubleWordRegister("Reg4"));
        block2.AddRegister(new DoubleWordRegister("Reg5"));
         _blocks.Add(block2);
    }
}

However I'm getting the following error:

Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>' на строке _blocks.Add (block1) и аналогично на _blocks.Add (block2);

Какой у вас вопрос? Ошибка компилятора довольно очевидна. phoog

Ваш Ответ

4   ответа
0

ByteRegisterBlock : RegisterBlock<ByteRegister>

Это должно заставить ваш код работать, однако вы теряете некоторую гибкость.

12

you forgot to ask a question, Вы просто изложили кучу фактов. Я собираюсь предположить, что ваш вопрос "почему компилятор выдает эту ошибку?"

Компилятор выдает эту ошибку, потому что ее отсутствие приведет к сбою во время выполнения. Предположим, мы разрешили:

List<RegisterBlock<IRegister> _blocks = new List<RegisterBlock<IRegister>>();
RegisterBlock<ByteRegister> block1= new RegisterBlock<ByteRegister>();
_blocks.Add(block1);  // Illegal, but suppose it was legal.

Теперь, что останавливает это?

RegisterBlock<IRegister> block1Again = _blocks[0];

Ничего такого._blocks это списокRegisterBlock<IRegister>ну и конечно_blocks[0] имеет типRegisterBlock<IRegister>, Но помните, что, конечно, первый пункт в списке на самом делеRegisterBlock<ByteRegister>.

Теперь, что останавливает это?

block1Again.AddRegister(new DoubleWordRegister())?

Ничего такого.block1Again имеет типRegisterBlock<IRegister>, который имеет методAddRegister(IRegister), а такжеDoubleWordRegister инвентарьIRegister.

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

Понятно, что это не безопасно. Единственное место, где это можно сделать незаконным во время компиляции, - это первый шаг; ковариантное преобразование не является законным в первую очередь.

Кстати, ваш вопрос часто задают здесь несколько раз в день. Дважды до сегодняшнего утра

Реализация вложенных универсальных интерфейсов

2

поэтому вы не можете явно пометить свойRegisterBlock<> ковариантный поT так, как вы хотите.

Однако вы на самом деле неneed в этом случае вам просто нужно объявить два объекта коллекции как:

RegisterBlock<IRegister> block1= new RegisterBlock<IRegister>

Поскольку обаByteRegister а такжеDoubleWordRegister воплощать в жизньIRegister Вы можете добавить любой из них вRegisterBlock<IRegister>

6

** проблема дисперсии, Вам понадобится другой интерфейс для вашегоRegisterBlock класс, может бытьIRegisterBlock:

public class RegisterBlock<T> : IRegisterBlock
    where T : IRegister

Затем вы можете создать списокIRegisterBlock:

private List<IRegisterBlock> _blocks;

На прошлой неделе у меня была похожая ситуация в нашей кодовой базе, и именно так я и решил ее.

Error: User Rate Limit Exceeded

Похожие вопросы