无法从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];
没有。 _blocks
是RegisterBlock
的列表,因此_blocks[0]
的类型为RegisterBlock
。 但请记住,列表中的第一项实际上是RegisterBlock
。
现在停止了什么?
block1Again.AddRegister(new DoubleWordRegister())?
没有。 block1Again
的类型为RegisterBlock
,它有一个方法AddRegister(IRegister)
, DoubleWordRegister
实现IRegister
。
所以你只需将一个双字寄存器放入一个只能包含字节寄存器的块中。
显然这不安全。 在编译时唯一可以成为非法的地方是第一步; 协变转换首先是不合法的。
顺便提一下,您的问题通常会在这里每天多次询问。 今天早上到目前为止两次:
实现嵌套通用接口
只有接口在C#中可以是协变的或逆变的,所以你不能以你想要的方式在T
上明确地标记你的RegisterBlock<>
协变。
但是,在这种情况下,您并不需要协方差,只需要将两个集合对象声明为:
RegisterBlock block1= new RegisterBlock
由于ByteRegister
和DoubleWordRegister
实现了IRegister
,因此可以将它们中的任何一个添加到RegisterBlock
也许如果你做了类似的事情。
ByteRegisterBlock : RegisterBlock
这应该可以使您的代码正常工作,但是,您确实会失去一些灵活性。