2011-05-09 17 views
5

Tengo algunas interfaces genéricas y las clases que implementan las interfases de este modo:C# de fundición con los genéricos que utilizan interfaces de

interface A<M, N> 
     where M : X<N> 
     where N : Y 
    { 
    } 
    class B<M, N> : A<M, N> 
     where M : X<N> 
     where N : Y 
    { 

    } 

    interface X<M> where M : Y 
    { 

    } 
    interface Y 
    { 

    } 
    class X1<M> : X<M> where M : Y 
    { 

    } 
    class Y1 : Y 
    { 

    } 

sé que parece ser una manera muy desordenado de hacer las cosas, pero en cierto modo me lo necesitan para mi aplicación Mi pregunta es ¿cómo es que no puedo hacer esto:

A<X<Y>, Y> variable = new B<X1<Y1>, Y1>();

Respuesta

8

Marc tiene razón; solo para darle más información sobre por qué esto no puede funcionar. Considere el siguiente cambio de nombre de su código:

interface IZoo<TCage, TAnimal> 
     where TCage : ICage<TAnimal> 
     where TAnimal : IAnimal 
    { 
    } 

    class Zoo<TCage, TAnimal> : IZoo<TCage, TAnimal> 
     where TCage : ICage<TAnimal> 
     where TAnimal : IAnimal 
    { 
    } 

    interface ICage<TAnimal> where TAnimal : IAnimal 
    { 
    } 

    interface IAnimal 
    { 
    } 

    class FishTank<TAnimal> : ICage<TAnimal> where TAnimal : IAnimal 
    { 
    } 

    class Fish : IAnimal 
    { 
    } 

Y ahora la pregunta es, ¿por qué no es esto legal:

Zoo<FishTank<Fish>, Fish> aquarium = new Zoo<FishTank<Fish>, Fish>(); 
IZoo<ICage<IAnimal>, IAnimal> zoo = aquarium; 

?

Porque supongamos ahora hay un método en iZoo:

interface IZoo<TCage, TAnimal> 
     where TCage : ICage<TAnimal> 
     where TAnimal : IAnimal 
    { 
     void PutAnimalInCage(TCage cage, TAnimal animal); 
    } 

Y luego dicen:

zoo.PutAnimalInCage(giraffePaddock, giraffe); 

Y sólo hay que poner un prado jirafa en un acuario! No podemos mantener la seguridad de tipo en un mundo donde la conversión que desea es legal e IZoo puede tener cualquier método que elija.

Ahora, esto solo es peligroso porque IZoo tiene dicho método. Si no tiene dicho método, entonces tiene razón, eso podría ser perfectamente seguro. En C# 4.0 agregamos una característica al lenguaje para que pueda preguntar al compilador "verificar si esta interfaz se puede hacer de forma segura variante", al anotar los parámetros de tipo que desea que sean covariantes con "fuera", y los que desea ser contravariante con "en". Si lo hace, el compilador comprobará si la varianza que desea puede ser segura para el tipo. Si no puede, no permitirá la declaración del tipo.

La forma en que esta cuestión habitualmente aparece en StackOverflow es la gente que pregunta por qué es esto ilegal:

List<Giraffe> giraffes = new List<Giraffe>(); 
List<Mammal> mammals = giraffes; // illegal 

misma razón. Porque entonces nada te detiene más tarde

mammals.Add(new Tiger()); 

y acabas de agregar un tigre a una lista de jirafas. Mismo razonamiento, solo un caso mucho más simple.

8

varianza debe ser explícita (y requiere C# 4.0); por ejemplo, esto hace que sea compilar como covariante (tenga en cuenta las out modificadores en la interfaz):

Nótese, sin embargo, que esto también limita su interfaz de covarianza ...! No podría tener un método Add(M), por ejemplo, ya que tendría que ser contavariante (también conocido como in).

+0

hey, Cuando intento hacer eso, dice que es la característica del lenguaje C# 4.0 y me sale un error, ¿qué debo hacer? – Twinhelix

+0

@Twinhelix: ¿qué versión de Visual Studio está utilizando y a qué plataforma está apuntando? Funciona para mí en VS2010 targetting .NET 2.0. Antes de C# 4.0 ... la varianza no existía para casos muy limitados. –

+0

Requiere C# 4.0. –