2011-02-03 12 views
9

Estoy recibiendo el siguiente error:¿Puedo implementar una interfaz que contenga una propiedad que sea de tipo hijo a la requerida por la interfaz?

ClassName.PropertyName cannot implement IClassType.PropertyName because it does not have the matching return type of IBasePropertyType

Ahora, para el código:

public class ClassName : IClassType 
{ 
    public IChildPropertyType PropertyName { get; set; } 
} 

public interface IClassType 
{ 
    public IBasePropertyType PropertyName { get; set; } 
} 

public interface IBasePropertyType 
{ 
    // some methods 
} 

public interface IChildPropertyType : IBasePropertyType 
{ 
    // some methods 
} 

¿Hay una manera de hacer lo que estoy tratando? Sé que el problema es con la co/contravariancia, pero parece que no puedo encontrar la manera de hacerlo.

+0

¿Cuál es la pregunta? – Victor

+0

@Victor: ¿Hay alguna manera de implementar una interfaz, pero tener una propiedad de tipo secundario para el tipo de propiedad definida en la interfaz? –

Respuesta

7

Para implementar la interfaz dada, debe tener el mismo tipo de devolución. Sin embargo, hay un par de posibles soluciones temporales para hacer la vida más fácil:

  1. hacer su interfaz genérica
  2. implementar la interfaz de forma explícita.

Si haces IClassType genérica, así:

public interface IClassType<T> where T : IBasePropertyType 
{ 
    public T PropertyName { get; set; } 
} 

... entonces se puede implementar esta interfaz utilizando diversos tipos de propiedad:

public class ClassName : IClassType<IChildPropertyType> 
{ 
    public IChildPropertyType PropertyName { get; set; } 
} 

Otra opción sería la de dejar su interfaz no genérica, pero para tener un tipo base genérico que implemente explícitamente la interfaz:

public class ClassBase<T> : IClassType 
    where T : IChildPropertyType 
{ 
    IBasePropertyType IClassType.PropertyName { 
     get {return PropertyName;} 
     set {PropertyName = (IChildPropertyType)value;} 
    } 
    T PropertyName {get;set;} 
} 

Tenga en cuenta que esta última opción no es del todo idónea porque debe convertir dinámicamente la propiedad al tipo de niño dado: mientras que puede garantizar que cada tipo de propiedad IChild sea IBasePropertyType, no puede garantizar que cada IBasePropertyType sea IChildPropertyType. Sin embargo, si puede eliminar el colocador de la interfaz original, o si puede tomar otras medidas para garantizar que nunca se llamará al colocador con el tipo incorrecto en su código, entonces esto podría funcionar.

+0

He intentado la ruta genérica, pero todavía no he reunido todas las piezas (o no estaría aquí). Gracias, y espero ver ejemplos de código que lo hagan funcionar. –

+0

@Mark Avenius: allí están. Dime que piensas. – StriplingWarrior

+0

borré mi respuesta ya que la suya es mucho mejor –

4

Tiene razón en que esto tiene que ver con la covarianza; específicamente tiene que ver con método virtual return type covariance, que no es un tipo de covarianza compatible con el lenguaje C#.

Tenga en cuenta que incluso si lo hizo, el sistema que describe no es seguro. Supongamos que tenemos:

interface IAnimal {} 
interface IGiraffe : IAnimal {} 
interface ITiger: IAnimal {} 
class Tiger : ITiger {} 
interface IHaveAnAnimal { IAnimal Animal { get; set; } } 
class C : IHaveAnAnimal 
{ 
    public IGiraffe Animal { get; set; } 
} 
... 
IHaveAnAnimal x = new C(); 
x.Animal = new Tiger(); // Uh oh. We just put a Tiger into a property of type IGiraffe. 

Incluso si la covarianza fuera legal en absoluto, este tipo de covarianza no sería legal; tendrías que tener no setter para que la covarianza sea legal.

Supongamos entonces tenías ningún colocador:

interface IAnimal {} 
interface IGiraffe : IAnimal {} 
interface ITiger: IAnimal {} 
class Tiger : ITiger {} 
interface IHaveAnAnimal { IAnimal Animal { get; } } 
class C : IHaveAnAnimal 
{ 
    public IGiraffe Animal { get; } 
} 

Desafortunadamente esto todavía no es legal. Pero se puede hacer esto:

class C : IHaveAnAnimal 
{ 
    IAnimal IHaveAnAnimal.Animal { get { return this.Animal; } } 
    public IGiraffe Animal { get; } 
} 

Ahora cuando C se utiliza como C, Animal devuelve una jirafa, y cuando se utiliza un un IHaveAnAnimal, devuelve un IAnimal.

Cuestiones relacionadas