2011-06-17 12 views
9

Pregunta de seguimiento a a previous question; esto se ha identificado como un problema de covarianza. Teniendo esto un paso más allá, si modifico IFactory como sigue:¿Por qué la lista <T> no es válida en una interfaz covariante? MiInterfaz <out T>

class Program 
{ 
    static void Main(string[] args) 
    { 
     IFactory<IProduct> factory = new Factory(); 
    } 
} 

class Factory : IFactory<Product> 
{ 
} 

class Product : IProduct 
{ 
} 

interface IFactory<out T> where T : IProduct 
{ 
    List<T> MakeStuff(); 
} 

interface IProduct 
{ 
} 

consigo:

varianza no válido: El parámetro de tipo T debe ser invariantly válida en Sandbox.IFactory.MakeStuff(). T es covariante.

¿Por qué esto no es invariablemente válido? ¿Cómo puedo/debo resolver esto?

Respuesta

7

@Craig's answer es correcto. Para resolver, cambiarlo a:

IEnumerable<T> MakeStuff() 

EDIT: En cuanto al por qué, mira la definición de IEnumerable<T> Interface:

public interface IEnumerable<out T> : IEnumerable 

Tenga en cuenta que la IList<T> Interface no tiene la palabra clave a cabo. La varianza es compatible con los parámetros de tipo genérico en interfaces y delegados, no en clases, por lo que no se aplica a la lista <T>.

+2

Tenga en cuenta que después de escribir esta respuesta, se ha lanzado una nueva versión de .NET Framework en la que 'List ' implementa una nueva interfaz 'IReadOnlyList '. Como se indicó, la interfaz es covariante al igual que 'IEnumerable '. –

6

Como puede agregar y eliminar objetos de un List<T>, T siempre debe ser invariable, incluso si la lista es un resultado de la función. Después de haber ejecutado var l = MakeStuff(), puede colocar cosas en la lista o sacarlas, por lo que T debe ser invariante.

+0

cómo resolver esto? – Firoso

+0

Utilice un tipo de devolución diferente para 'MakeStuff'. –

11

Las otras respuestas son correctas, pero es instructivo razonar por qué el compilador señala esto como inseguro. Supongamos que lo permitimos; ¿qué puede salir mal?

class Sprocket: Product {} 
class Gadget : Product {} 
class GadgetFactory : IFactory<Gadget> 
{ 
    public List<Gadget> MakeStuff() 
    { 
     return new List<Gadget>() { new Gadget(); } 
    } 
} 
... later ... 
IFactory<Gadget> gf = new GadgetFactory(); 
IFactory<Product> pf = gf; // Covariant! 
List<Product> pl = pf.MakeStuff(); // Actually a list of gadgets 
pl.Add(new Sprocket()); 

y hey, acabamos de agregar un piñón a una lista que solo puede contener gadgets.

Solo hay un lugar donde el compilador puede detectar el problema, y ​​eso está en la declaración de la interfaz.

Disculpe el mensaje de error algo excesivamente jerga. No podría pensar en nada mejor.

+0

Gracias por los comentarios, agradezco tener más antecedentes, parece que no entiendo co y contravariancia tan bien como lo había pensado. Siempre es bueno tener más antecedentes, ahora libera la parte 4 de tu serie de inmutabilidad :-P – Firoso

Cuestiones relacionadas