Cannot convert from generic type to interface

I get an error when trying to add a shared object to the list <>.

This is probably due to covariance and contravariance, but I'm not sure how to get around this. I tried to restrict my generic types using T: IRegister.

I have an interface to represent a register, and then two classes that are ByteRegister and DoubleWordRegister.

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

Then I have another class that represents a block of these registers 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 that is used to define a list of register blocks and each register in a block.

 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 get the following error:

Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>' in the line _blocks.Add (block1) and similarly on _blocks.Add (block2);

+6
source share
4 answers

This is really a problem ** . You will need a different interface for your RegisterBlock class, possibly IRegisterBlock :

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

Then you can create an IRegisterBlock list:

 private List<IRegisterBlock> _blocks; 

Last week I had a similar situation in our code base, and that’s how I resolved it.

+5
source

I notice that you forgot to ask a question . You just stated a bunch of facts. I'm going to suggest that your question is: “Why is the compiler creating this error?”

The compiler generates this error because failing to produce this error will result in a run-time failure. 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 RegisterBlock<IRegister> list, so of course _blocks[0] is of type RegisterBlock<IRegister> . But remember that, of course, the first item on the list is actually RegisterBlock<ByteRegister> .

Now, what stops this?

 block1Again.AddRegister(new DoubleWordRegister())? 

Nothing. block1Again is of type RegisterBlock<IRegister> , which has an AddRegister(IRegister) method, and DoubleWordRegister implements IRegister .

So, you just put the double word register in a block that can only contain byte registers.

Clearly, this is unsafe. The only place where this can be made illegal during compilation is the first step; covariant transformation is not legal in the first place.

By the way, your question is often asked several times a day here. This morning:

Implement Nested Common Interfaces

+12
source

Only interfaces can be covariant or contravariant in C #, so you cannot explicitly point your RegisterBlock<> covariator to T way you want.

However, in this case, you do not need covariance, you just need to declare your two collection objects as follows:

 RegisterBlock<IRegister> block1= new RegisterBlock<IRegister> 

Since both ByteRegister and DoubleWordRegister implement IRegister , you can add either of them to RegisterBlock<IRegister>

+2
source

Perhaps if you did something like.

 ByteRegisterBlock : RegisterBlock<ByteRegister> 

This should make your code work, however you are losing some flexibility.

0
source

Source: https://habr.com/ru/post/914347/


All Articles