简体   繁体   English

无法从通用类型转换为接口

[英]Cannot convert from generic type to interface

I'm getting an error when trying to add generic object to a List<>. 尝试将通用对象添加到List <>时出现错误。

Its probably related to Covariance and Contravariance but I not sure how to work around this. 它可能与协方差和协方差有关,但我不确定如何解决。 I've tried to restrict my generic types using where T: IRegister . 我试图使用where T:IRegister来限制我的泛型类型。

I have an interface to represent a register and then two classes which represent a ByteRegister and a DoubleWordRegister. 我有一个代表寄存器的接口,然后有两个代表ByteRegister和DoubleWordRegister的类。

public interface IRegister
{
    string Name {get;set;}
}

 public class ByteRegister : IRegister
{
...
}

public class DoubleWordRegister : IRegister
{
...
}

I then have another class which represents a block of these registers all of the same type. 然后,我有另一个类,代表这些寄存器的全部相同类型的块。

public class RegisterBlock<T> where T:IRegister
{
   private IList<T> _registers;

 ... constructors, properties etc

    public void AddRegister(T register)
    {
        _registers.Add(register);
    }
}

And finally I have a RegisterMap class which is used to define the list of register blocks and each register within the block. 最后,我有一个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);
    }
}

However I'm getting the following error: 但是我收到以下错误:

Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>' on the line _blocks.Add(block1) and similarly on _blocks.Add(block2); Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>'在行_blocks.Add(block1)以及类似地在_blocks.Add(block2)上Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>'

I notice that you forgot to ask a question . 我注意到你忘了问一个问题 You merely stated a bunch of facts. 您只说了一堆事实。 I'm going to assume your question is "why does the compiler produce this error?" 我将假设您的问题是“为什么编译器会产生此错误?”

The compiler produces that error because to not produce that error would lead to a crash at runtime. 编译器会产生该错误,因为不产生该错误将导致运行时崩溃。 Suppose we allowed: 假设我们允许:

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

Now what stops this? 现在,这阻止了什么?

RegisterBlock<IRegister> block1Again = _blocks[0];

Nothing. 没有。 _blocks is a list of RegisterBlock<IRegister> , so of course _blocks[0] is of type RegisterBlock<IRegister> . _blocksRegisterBlock<IRegister>的列表,因此_blocks[0]当然是RegisterBlock<IRegister>类型的。 But remember that of course the first item in the list is actually a RegisterBlock<ByteRegister> . 但是请记住,当然,列表中的第一项实际上是RegisterBlock<ByteRegister>

Now what stops this? 现在,这阻止了什么?

block1Again.AddRegister(new DoubleWordRegister())?

Nothing. 没有。 block1Again is of type RegisterBlock<IRegister> , which has a method AddRegister(IRegister) , and DoubleWordRegister implements IRegister . block1Again的类型为RegisterBlock<IRegister> ,它具有方法AddRegister(IRegister) ,而DoubleWordRegister实现IRegister

So you just put a double word register into a block that can only contain byte registers. 因此,只需将双字寄存器放入只能包含字节寄存器的块中。

Clearly that is not safe. 显然这是不安全的。 The only place where that can be made illegal at compile time is in the first step; 在编译时唯一可以使之非法的地方是第一步。 the covariant conversion is not legal in the first place. 首先,协变转换是不合法的。

Incidentally, your question is often asked several times a day here. 顺便说一句,您的问题经常一天在这里被问过几次。 Twice so far this morning: 到今天早上有两次:

Implementing nested generic Interfaces 实现嵌套的通用接口

This is indeed a **variance problem . 这确实是一个方差问题 You will need another interface for your RegisterBlock class, maybe IRegisterBlock : 您将为RegisterBlock类需要另一个接口,也许是IRegisterBlock

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

Then you can create a list of IRegisterBlock : 然后,您可以创建IRegisterBlock的列表:

private List<IRegisterBlock> _blocks;

I actually had a similar situation in our code-base last week, and this is exactly how I resolved it. 上周我在代码库中实际上遇到了类似情况,这正是我解决问题的方法。

Only interfaces can be covariant or contravariant in C#, so you can't explicitly mark your RegisterBlock<> covariant on T the way you want. 在C#中,只有接口可以是协变的,也可以是协变的,因此您不能在T上显式标记所需的RegisterBlock<>协变。

However, you don't really need covariance in this case, you just need to declare your two collection objects as: 但是,在这种情况下,您实际上并不需要协方差,只需声明两个集合对象为:

RegisterBlock<IRegister> block1= new RegisterBlock<IRegister>

Since both ByteRegister and DoubleWordRegister implement IRegister you can add either of them to a RegisterBlock<IRegister> 由于ByteRegisterDoubleWordRegister实现了IRegister您可以将它们之一添加到RegisterBlock<IRegister>

Maybe if you did something like. 也许您做了类似的事情。

ByteRegisterBlock : RegisterBlock<ByteRegister>

This should get your code working, however, you do lose some flexibility. 这样可以使您的代码正常工作,但是,您确实失去了灵活性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM