2012-02-20 11 views
13

ACTUALIZACIÓN: El siguiente código sólo tiene sentido en C# 4.0 (Visual Studio 2010)covarianza/contravarianza: cómo hacer el siguiente código compilar

Parece que estoy teniendo un mal entendido de la covarianza/cosa contravarianza . ¿Alguien puede decirme por qué el siguiente código no se compila?

public class TestOne<TBase> 
{ 
    public IEnumerable<TBase> Method<TDerived>(IEnumerable<TDerived> values) 
     where TDerived: TBase 
    { 
     return values; 
    } 
} 

mientras éste compila: (!!!)

public interface IBase 
{ 
} 
public interface IDerived: IBase 
{ 
} 
public class TestTwo 
{ 
    public IEnumerable<IBase> Method(IEnumerable<IDerived> values) 
    { 
     return values; 
    } 
} 

Respuesta

13

covarianza sólo se aplica a tipos-referencia (para los argumentos de tipo), por lo que hay que añadir una restricción de clase:

public IEnumerable<TBase> Method<TDerived>(IEnumerable<TDerived> values) 
    where TDerived : class, TBase 
{ 
    return values; 
} 

Esto le impedirá tratar de convertir, por ejemplo, un IEnumerable<int> en un IEnumerable<object>, que no es válido.

+0

@ Adam: Creo que estás equivocado, '' IEnumerable no es '' IEnumerable por defecto para que no se compile incluso en '3.5' – sll

+1

@AdamMihalcin: No, eso no lo haría código * * compilado antes de .NET 4. Lo he intentado solo para verificarlo. Sin invarianza genérica, la conversión de 'IEnumerable ' a 'IEnumerable 'es simplemente inválida. –

+0

buena llamada en TSuper, simplemente solucionado que –

1

No puedo pensar en ninguna situación en la que realmente necesite TDerived. Usando TBase es suficiente:

public class TestOne<TBase> 
{ 
    public IEnumerable<TBase> Method(IEnumerable<TBase> values) 
    { 
     return values; 
    } 
} 

Después de todo, usted no tiene ninguna información sobre TDerived aparte del hecho de que es un TBase ...

+2

lamentablemente no es algo que inventé en aras de la complejidad, es una situación real que terminé teniendo en mi código –

+1

@bonomo: Eso es interesante, me gustaría ver un ejemplo más detallado para referencia futura. :-) – linepogl

0

Ni compilado para mí al principio. Ambos fallaron en el reparto implícito de Super (T/I) a Base (T/I). Cuando agregué un caso explícito, sin embargo, ambos compilaron.

public IEnumerable<TBase> Method<TSuper>(IEnumerable<TSuper> values) 
    where TSuper: TBase 
    { 
     return (IEnumerable<TBase>) values; 
    } 
Cuestiones relacionadas