无法从generics类型转换为接口

我在尝试将通用对象添加到List 时遇到错误。

它可能与Covariance和Contravariance有关,但我不知道如何解决这个问题。 我试图用T:IRegister来限制我的generics类型。

我有一个接口来表示一个寄存器,然后是两个代表ByteRegister和DoubleWordRegister的类。

public interface IRegister { string Name {get;set;} } public class ByteRegister : IRegister { ... } public class DoubleWordRegister : IRegister { ... } 

然后,我有另一个类,它代表了所有相同类型的这些寄存器的块。

 public class RegisterBlock where T:IRegister { private IList _registers; ... constructors, properties etc public void AddRegister(T register) { _registers.Add(register); } } 

最后我有一个RegisterMap类,用于定义寄存器块列表和块内的每个寄存器。

 public class RegisterMap { private List<RegisterBlock> _blocks; public RegisterMap() { _blocks = new List<RegisterBlock>(); RegisterBlock block1= new RegisterBlock("Block1", 0); block1.AddRegister(new ByteRegister("Reg1")); block1.AddRegister(new ByteRegister("Reg2")); _blocks.Add(block1); RegisterBlock block2= new RegisterBlock("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' to 'RegisterBlock'在_blocks.Add(block1)行上Error 20 Argument '1': cannot convert from 'RegisterBlock' to 'RegisterBlock' ,类似地在_blocks.Add(block2)上转换;

这确实是一个**方差问题 。 您将需要RegisterBlock类的另一个接口,也许是IRegisterBlock

 public class RegisterBlock : IRegisterBlock where T : IRegister 

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

 private List _blocks; 

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

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

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

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

现在停止了什么?

 RegisterBlock block1Again = _blocks[0]; 

没有。 _blocksRegisterBlock的列表,因此_blocks[0]的类型为RegisterBlock 。 但请记住,列表中的第一项实际上是RegisterBlock

现在停止了什么?

 block1Again.AddRegister(new DoubleWordRegister())? 

没有。 block1Again的类型为RegisterBlock ,它有一个方法AddRegister(IRegister)DoubleWordRegister实现IRegister

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

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

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

实现嵌套通用接口

只有接口在C#中可以是协变的或逆变的,所以你不能以你想要的方式在T上明确地标记你的RegisterBlock<>协变。

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

 RegisterBlock block1= new RegisterBlock 

由于ByteRegisterDoubleWordRegister实现了IRegister ,因此可以将它们中的任何一个添加到RegisterBlock

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

 ByteRegisterBlock : RegisterBlock 

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