我在尝试将通用对象添加到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);
答案 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];
无。 _blocks
是RegisterBlock<IRegister>
的列表,当然_blocks[0]
的类型为RegisterBlock<IRegister>
。但请记住,当然列表中的第一项实际上是RegisterBlock<ByteRegister>
。
现在是什么阻止了这个?
block1Again.AddRegister(new DoubleWordRegister())?
无。 block1Again
类型为RegisterBlock<IRegister>
,其方法为AddRegister(IRegister)
,DoubleWordRegister
实现IRegister
。
所以你只需将一个双字寄存器放入一个只能包含字节寄存器的块中。
显然这不安全。在编译时唯一可以成为非法的地方是第一步;协变转换首先是不合法的。
顺便提一下,您的问题通常会在这里每天多次询问。今天早上到目前为止两次:
答案 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>
由于ByteRegister
和DoubleWordRegister
都实现IRegister
,您可以将其中任何一个添加到RegisterBlock<IRegister>
答案 3 :(得分:0)
也许你做过像。
ByteRegisterBlock : RegisterBlock<ByteRegister>
这应该让您的代码正常工作,但是,您确实失去了一些灵活性。