2010-03-03 12 views
6

he especificado un par de interfaces, que me estoy poniendo en práctica como entidades utilizando Entity Framework 4. El código de demostración simple que puede llegar a es:Contravariance y Entity Framework 4.0: cómo especificar EntityCollection como IEnumerable?

public class ConcreteContainer : IContainer 
{ 
    public EntityCollection<ConcreteChild> Children { get; set; }   
} 
public class ConcreteChild : IChild 
{ 
} 
public interface IContainer 
{ 
    IEnumerable<IChild> Children { get; set; } 
} 
public interface IChild 
{   
} 

recibo el siguiente error del compilador de lo anterior:

'Demo.ConcreteContainer' no no implementa miembro de interfaz 'Demo.IContainer.Children'. '' Demo.ConcreteContainer.Children no se puede poner en práctica '' Demo.IContainer.Children porque no tiene la relación del tipo de retorno de

Mi comprensión actual 'System.Collections.Generic.IEnumerable' es que esto se debe a IEnumerable (que se implementa por EntityCollection) es covariante pero se supone que no contravariant:

este tipo de parámetro es covariante. Es decir, puede usar ya sea del tipo que especificó o de cualquier tipo que sea más derivado. Para obtener más información acerca de la covarianza y la contravarianza, vea Covarianza y contravarianza en genéricos.

Estoy en lo correcto, & si es así, ¿hay alguna manera de que pueda lograr mi objetivo de especificar la interfaz IContainer puramente en términos de otras interfaces en lugar de utilizar clases concretas?

O, ¿estoy malentendiendo algo más fundamental?

Respuesta

5

La varianza genérica en .NET 4 es irrelevante aquí. La implementación de una interfaz tiene que coincidir con la firma de interfaz exactamente en términos de tipos.

Por ejemplo, tomemos ICloneable, que se ve así:

public interface ICloneable 
{ 
    object Clone(); 
} 

Sería agradable para poder poner en práctica esta manera:

public class Banana : ICloneable 
{ 
    public Banana Clone() // Fails: this doesn't implement the interface 
    { 
     ... 
    } 
} 

... pero.NET no permite esto. A veces se puede utilizar explícita trabajo implementación de la interfaz alrededor de este, así:

public class Banana : ICloneable 
{ 
    public Banana Clone() 
    { 
     ... 
    } 

    object ICloneable.Clone() 
    { 
     return Clone(); // Delegate to the more strongly-typed method 
    } 
} 

Sin embargo, en su caso, puede que no vuelvas a hacer eso. Considere el siguiente código, que sería válida si ConcreteContainer se consideró para implementar IContainer:

IContainer foo = new ConcreteContainer(); 
foo.Children = new List<IChild>(); 

Ahora su colocador propiedad se declaró en realidad sólo para trabajar con EntityCollection<ConcreteChild>, por lo que es evidente que no puede trabajar con cualquier IEnumerable<IChild> - en violación de la interfaz.

4

Por lo que tengo entendido, debe implementar una interfaz; no puede suponer que un miembro covariante/contravariante sea recogido como un sustituto. Incluso si fue permitido, tenga en cuenta que setter para niños es un problema. Porque permitirá establecer la propiedad del tipo EntityCollection<ConcreteChild> con el valor de cualquier otro tipo como List<ConcreteChild> o EntityCollection<ConcreteChild2> porque ambos están implementando IEnumerable<IChild>.

En el diseño actual, implementaré IContainer de forma privada en ConcreteContainer y verificará el valor de entrada en IEnumerable.Children setter para un tipo compatible. Otra forma de abordar este diseño es tener interfaces genéricas, tales como:

public interface IContainer<T> where T:IChild 
{ 
    IEnumerable<T> Children { get; set; } 
} 
+1

Gracias por confirmar mis sospechas. El enfoque de la interfaz genérica es agradable, pero no funciona con EF4, lo que hace que EF4 sea bastante desafiante para simular en su totalidad cuando se prueban las unidades. Como resultado, nos hemos movido principalmente a pruebas de integración para entidades EF. –

0

Así que debe implementar esta interfaz, ¿verdad?

public interface IContainer 
{ 
    IEnumerable<IChild> Children { get; set; } 
} 

Pero en la clase de bienes, desea que la propiedad sea del tipo EntityCollection<ConcreteChild>. He aquí cómo puedes hacer esto:

public class ConcreteContainer : IContainer 
{ 
    // This is the property that will be seen by code that accesses 
    // this instance through a variable of this type (ConcreteContainer) 
    public EntityCollection<ConcreteChild> Children { get; set; }   

    // This is the property that will be used by code that accesses 
    // this instance through a variable of the type IContainer 
    IEnumerable<ConcreteChild> IContainer.Children { 
     get { return Children; } 
     set { 
      var newCollection = new EntityCollection<ConcreteChild>(); 
      foreach (var item in value) 
       newCollection.Add(item); 
      Children = newCollection; 
     } 
    } 
} 
Cuestiones relacionadas