2010-06-29 6 views
14

Ho bisogno di una classe base con una proprietà in cui è possibile derivare classi con la stessa proprietà ma tipi diversi (compatibili). La classe base può essere astratta.Ignora proprietà con diverso tipo compatibile

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>(); 
     //... 
    } 
} 

ho provato con Generics ma che mi dà un problema quando si utilizza la classe, perché voglio per memorizzare le classi di base in modo diverso digitati nella 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 
     //... 
    } 
} 

risposta

22

Ecco un approccio alternativo alla soluzione proposta:

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); 
    } 
} 

Fondamentalmente ho aggiunto una classe generica nel mezzo che memorizza la proprietà tipizzata correttamente. ciò funzionerà assumendo che non sia mai necessario accedere a Prop dal codice che itera i membri di List<Base>. (Si può sempre aggiungere un metodo astratto per Base chiamato GetProp che getta il generico a un oggetto, se ciò che è richiesto.) Utilizzo

Esempio:

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(); 
    } 
} 

Edit: Aggiunto il metodo GetProp() per illustrare in che modo è possibile accedere direttamente alla proprietà dalla classe base.

+0

+1: questa è una soluzione pulita al problema in questione. Separa la classe base dalla parte generica. –

6

Non è possibile sostituire il tipo di una proprietà. Date un'occhiata al seguente codice:

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

Codice completamente valido. Ma cosa succede quando provi a fare questo?

b.prop = 5; 

Il numero intero può essere convertito object, perché tutto è derivato da object. Ma dal momento che b è in realtà un'istanza StrBase, dovrebbe convertire il numero intero in una stringa in qualche modo, che non può. Ecco perché non ti è consentito sovrascrivere il tipo.

Lo stesso principio vale per i farmaci generici:

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; 

Questo perché tipi generici in C# 2.0 sono invarianti. C# 4.0 consente questo tipo di conversioni, chiamate covarianza e controvarianza.

Solutions

Una possibilità è quella di lanciare il object torna a stringa quando ne avete bisogno. Si potrebbe aggiungere convalida del tipo nella sottoclasse:

public class StrBase : Base 
{ 
    private string propValue; 

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

Si potrebbe anche esporre una proprietà type-safe nella sottoclasse:

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