无法从泛型类型转换为接口

时间:2012-04-27 15:31:13

标签: c# generics

我在尝试将通用对象添加到List<>。

时遇到错误

它可能与Covariance和Contravariance有关,但我不知道如何解决这个问题。我试图使用来限制我的泛型类型,其中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);
    }
}

但是我收到以下错误:

Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>'在_blocks.Add(block1)行上,类似于_blocks.Add(block2);

4 个答案:

答案 0 :(得分:12)

我注意到你忘记提问。你只是陈述了一堆事实。我将假设您的问题是“为什么编译器会产生此错误?”

编译器会产生该错误,因为不会产生该错误会导致运行时崩溃。假设我们允许:

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];

无。 _blocksRegisterBlock<IRegister>的列表,当然_blocks[0]的类型为RegisterBlock<IRegister>。但请记住,当然列表中的第一项实际上是RegisterBlock<ByteRegister>

现在是什么阻止了这个?

block1Again.AddRegister(new DoubleWordRegister())?

无。 block1Again类型为RegisterBlock<IRegister>,其方法为AddRegister(IRegister)DoubleWordRegister实现IRegister

所以你只需将一个双字寄存器放入一个只能包含字节寄存器的块中。

显然这不安全。在编译时唯一可以成为非法的地方是第一步;协变转换首先是不合法的。

顺便提一下,您的问题通常会在这里每天多次询问。今天早上到目前为止两次:

Implementing nested generic Interfaces

答案 1 :(得分:6)

这确实是**variance problem。您需要为RegisterBlock课程设置另一个界面,可能是IRegisterBlock

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

然后您可以创建IRegisterBlock的列表:

private List<IRegisterBlock> _blocks;

上周我的代码库实际上有类似的情况,这正是我解决它的原因。

答案 2 :(得分:2)

在C#中只有接口可以是协变或逆变的,因此您无法以{1}的方式在RegisterBlock<>上明确标记T协变。

但是,在这种情况下,你并不需要需要协方差,你只需要将两个集合对象声明为:

RegisterBlock<IRegister> block1= new RegisterBlock<IRegister>

由于ByteRegisterDoubleWordRegister都实现IRegister,您可以将其中任何一个添加到RegisterBlock<IRegister>

答案 3 :(得分:0)

也许你做过像。

ByteRegisterBlock : RegisterBlock<ByteRegister>

这应该让您的代码正常工作,但是,您确实失去了一些灵活性。