7

尝试将通用对象添加到 List<> 时出现错误。

它可能与协方差和逆变有关,但我不知道如何解决这个问题。我尝试使用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);
    }
}

但是我收到以下错误:

Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>'在 _blocks.Add(block1) 线上,同样在 _blocks.Add(block2);

4

4 回答 4

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

没有什么。_blocks是一个列表RegisterBlock<IRegister>,所以当然_blocks[0]是类型RegisterBlock<IRegister>。但请记住,当然列表中的第一项实际上是一个RegisterBlock<ByteRegister>.

现在是什么阻止了这一切?

block1Again.AddRegister(new DoubleWordRegister())?

没有什么。block1Again是类型RegisterBlock<IRegister>,它有一个方法AddRegister(IRegister)DoubleWordRegister实现IRegister

因此,您只需将双字寄存器放入只能包含字节寄存器的块中。

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

顺便说一句,您的问题每天在这里经常被问好几次。今天早上两次:

实现嵌套的通用接口

于 2012-04-27T15:59:06.563 回答
6

这确实是一个**方差问题。您的RegisterBlock班级可能需要另一个界面,也许是IRegisterBlock

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

然后你可以创建一个列表IRegisterBlock

private List<IRegisterBlock> _blocks;

实际上,上周我在我们的代码库中遇到了类似的情况,这正是我解决它的方法。

于 2012-04-27T15:34:42.700 回答
2

在 C# 中,只有接口可以是协变或逆变的,因此您不能以您想要的方式显式标记您的RegisterBlock<>协变。T

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

RegisterBlock<IRegister> block1= new RegisterBlock<IRegister>

由于两者都ByteRegisterDoubleWordRegister实现IRegister,您可以将它们中的任何一个添加到RegisterBlock<IRegister>

于 2012-04-27T15:41:42.860 回答
0

也许如果你做了类似的事情。

ByteRegisterBlock : RegisterBlock<ByteRegister>

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

于 2012-04-28T00:58:16.970 回答