2010-06-29 20 views
14

Necesito una clase base con una propiedad donde puedo derivar clases con la misma propiedad pero tipos diferentes (compatibles). La clase base puede ser abstracta.Anular propiedad con diferente tipo compatible

public class Base 
{ 
    public virtual object prop { get; set; } 
} 

public class StrBase : Base 
{ 
    public override string prop { get; set; } // compiler error 
} 

public class UseIt 
{ 
    public void use() 
    { 
     List<Base> l = new List<Base>(); 
     //... 
    } 
} 

he probado con los genéricos, pero eso me da un problema cuando se utiliza la clase, porque quiero almacenar clases base diferente mecanografiadas en la Lista.

public class BaseG<T> 
{ 
    public T prop { get; set; } 
} 

public class UseIt 
{ 
    public void use() 
    { 
     List<BaseG> l = new List<BaseG>(); // requires type argument 
     //... 
    } 
} 

Respuesta

22

He aquí un enfoque alternativo a la solución propuesta:

public abstract class Base 
{ 
    public abstract void Use(); 
    public abstract object GetProp(); 
} 

public abstract class GenericBase<T> : Base 
{ 
    public T Prop { get; set; } 

    public override object GetProp() 
    { 
     return Prop; 
    } 
} 

public class StrBase : GenericBase<string> 
{ 
    public override void Use() 
    { 
     Console.WriteLine("Using string: {0}", Prop); 
    } 
} 

public class IntBase : GenericBase<int> 
{ 
    public override void Use() 
    { 
     Console.WriteLine("Using int: {0}", Prop); 
    } 
} 

Básicamente, he agregado una clase genérica en el medio que almacena su propiedad correctamente tipada. esto funcionará suponiendo que nunca necesita acceder al Prop desde el código que itera a los miembros del List<Base>. (Siempre se puede añadir un método abstracto a Base llamada GetProp que arroja el genérica a un objeto si lo que se requiere.) El uso

muestra:

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Base> l = new List<Base>(); 

     l.Add(new StrBase {Prop = "foo"}); 
     l.Add(new IntBase {Prop = 42}); 

     Console.WriteLine("Using each item"); 
     foreach (var o in l) 
     { 
      o.Use(); 
     } 
     Console.WriteLine("Done"); 
     Console.ReadKey(); 
    } 
} 

Editar: Se ha añadido el método GetProp() para ilustrar cómo se puede acceder directamente a la propiedad desde la clase base.

+0

+1: Esta es una solución limpia al problema en cuestión. Separe la clase base de la parte genérica. –

6

No puede anular el tipo de propiedad. Eche un vistazo al siguiente código:

StrBase s = new StrBase(); 
Base b = s; 

Este es un código completamente válido. ¿Pero qué pasa cuando tratas de hacer esto?

b.prop = 5; 

El número entero se puede convertir en object, porque todo se deriva de object. Pero dado que b es en realidad una instancia de StrBase, debería convertir el entero a una cadena de alguna manera, lo cual no es posible. Es por eso que no se te permite anular el tipo.

El mismo principio se aplica a los genéricos:

List<BaseG<object>> l = new List<BaseG<object>>(); 
BaseG<string> s = new BaseG<string>(); 

// The compiler will not allow this. 
l.add(s); 

// Here's the same problem, convert integer to string? 
BaseG<object> o = l[0]; 
o.prop = 5; 

Esto se debe a los tipos genéricos en C# 2.0 son invariantes. C# 4.0 permite este tipo de conversiones, llamadas covarianza y contravarianza.

Soluciones

Una opción es a emitir el object de nuevo a cadena cuando lo necesite. Se podría añadir validación de tipo en la subclase:

public class StrBase : Base 
{ 
    private string propValue; 

    public override object prop { 
    get 
    { 
     return this.propValue; 
    } 
    set 
    { 
     if (value is string) 
     { 
     this.propValue = (string)value; 
     } 
    } 
    } 
} 

También podría exponer una propiedad de tipo seguro en la subclase:

public class StrBase : Base 
{ 
    public string strProp { 
    get 
    { 
     return (string)this.prop; 
    } 
    set 
    { 
     this.prop = value; 
    } 
    } 
} 
Cuestiones relacionadas