2012-04-27 33 views
6

Recibo un error al intentar agregar un objeto genérico a una lista <>.No se puede convertir de tipo genérico a interfaz

Probablemente esté relacionado con la covarianza y la contradicción pero no estoy seguro de cómo solucionar esto. Intenté restringir mis tipos genéricos usando donde T: IRegister.

Tengo una interfaz para representar un registro y luego dos clases que representan un ByteRegister y un DoubleWordRegister.

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

public class ByteRegister : IRegister 
{ 
... 
} 

public class DoubleWordRegister : IRegister 
{ 
... 
} 

Luego tengo otra clase que representa un bloque de estos registros del mismo tipo.

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

... constructors, properties etc 

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

Y finalmente tengo una clase RegisterMap que se utiliza para definir la lista de bloques de registro y cada registro dentro del bloque.

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); 
    } 
} 

Sin embargo estoy recibiendo el siguiente error:

Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>' en la línea _blocks.Add (Bloque 1) y de manera similar en _blocks.Add (Bloque 2);

+1

¿Cuál es tu pregunta? El error del compilador es bastante claro. – phoog

Respuesta

5

Esto es de hecho un **variance problem. Necesitará otra interfaz para su clase RegisterBlock, tal vez IRegisterBlock:

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

continuación, puede crear una lista de IRegisterBlock:

private List<IRegisterBlock> _blocks; 

en realidad tenía una situación similar en nuestro código base la semana pasada, y así es exactamente como lo resolví

+0

Guau .. Nunca supe/tropecé con el ** problema de varianza antes de hoy. Normalmente tiendo a NO usar IEnumerable ... pero ahora sé exactamente cuándo usarlo. Gracias ... ¡Creo que aprendí algo realmente importante hoy! Mi método esperaba una lista donde List es contravariante, por lo que usar IEnumarable tiene perfecto sentido +1 – ppumkin

2

Las únicas interfaces pueden ser covariantes o contravariantes en C#, por lo que no puede marcar explícitamente su covariante RegisterBlock<> en T de la manera que desee.

Sin embargo, realmente no necesidad de covarianza en este caso, sólo tiene que declarar sus dos objetos de colección como:

RegisterBlock<IRegister> block1= new RegisterBlock<IRegister> 

Dado que tanto ByteRegister y DoubleWordRegister aplicar IRegister puede añadir cualquiera de ellos a un RegisterBlock<IRegister>

12

Me di cuenta de que se olvidó de hacer una pregunta. Simplemente mencionaste un montón de hechos. Voy a suponer que tu pregunta es "¿por qué el compilador produce este error?"

El compilador produce ese error porque no producir ese error provocaría un bloqueo en el tiempo de ejecución. Supongamos que permitimos:

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

Ahora, ¿qué detiene esto?

RegisterBlock<IRegister> block1Again = _blocks[0]; 

Nada._blocks es una lista de RegisterBlock<IRegister>, por supuesto _blocks[0] es del tipo RegisterBlock<IRegister>. Pero recuerde que, por supuesto, el primer elemento de la lista es en realidad un RegisterBlock<ByteRegister>.

Ahora, ¿qué detiene esto?

block1Again.AddRegister(new DoubleWordRegister())? 

Nada. block1Again es del tipo RegisterBlock<IRegister>, que tiene un método AddRegister(IRegister), y DoubleWordRegister implementa IRegister.

Así que simplemente coloca un registro de palabras dobles en un bloque que solo puede contener registros de bytes.

Claramente eso no es seguro. El único lugar donde puede hacerse ilegal en el momento de la compilación es en el primer paso; la conversión covariante no es legal en primer lugar.

Por cierto, su pregunta a menudo se hace varias veces al día aquí. Dos veces en lo que va de la mañana:

Implementing nested generic Interfaces

0

Tal vez si se hizo algo parecido.

ByteRegisterBlock : RegisterBlock<ByteRegister> 

Esto debería hacer que su código funcione, sin embargo, pierde cierta flexibilidad.

Cuestiones relacionadas