2010-06-29 9 views
14

Potrzebuję klasy bazowej z właściwością, w której mogę wyprowadzić klasy o tej samej właściwości, ale o różnych (zgodnych) typach. Klasa podstawowa może być abstrakcyjna.Zastąp właściwość z innym zgodnym typem.

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

próbowałem go z rodzajowych, ale to daje mi problem przy korzystaniu z klasy, ponieważ chcę, aby przechowywać inaczej wpisywanych klas bazowych na liście.

Odpowiedz

22

Oto alternatywne podejście do proponowanego rozwiązania:

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

Zasadniczo dodałem w środku klasę ogólną, która przechowuje prawidłowo wpisaną właściwość. to zadziała, zakładając, że nigdy nie potrzebujesz dostępu do Prop z kodu, który iteruje członków List<Base>. (Zawsze możesz dodać abstrakcyjnej metody do Base nazwie GetProp że rzuca generic do obiektu, jeżeli jest to wymagane). Wykorzystanie

próbki:

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

Edycja: Dodano metoda GetProp(), aby zilustrować jak dostęp do nieruchomości można uzyskać bezpośrednio z klasy bazowej.

+0

+1: To jest czyste rozwiązanie problemu. Oddziel klasę podstawową od części ogólnej. –

6

Nie można zastąpić typu właściwości. Zapoznaj się z następującym kodem:

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

To jest całkowicie poprawny kod. Ale co się dzieje, gdy próbujesz to zrobić?

b.prop = 5; 

Liczba całkowita może być przekształcany do object, ponieważ wszystko pochodzi z object. Ale ponieważ b jest w rzeczywistości instancją StrBase, musiałby w jakiś sposób przekonwertować wartość całkowitą na ciąg znaków, którego nie może. Dlatego właśnie nie wolno ci nadpisywać tego typu.

Ta sama zasada odnosi się do leków generycznych:

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; 

To dlatego typy rodzajowe w C# 2.0 są niezmienne. C# 4.0 pozwala na ten typ konwersji, zwany kowariancją i kontrawariancją.

Solutions

Opcja jest do oddania object powrotem do łańcucha, kiedy jest to potrzebne. Można dodać walidację typu na podklasy:

public class StrBase : Base 
{ 
    private string propValue; 

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

można również narazić właściwość type-safe w podklasie:

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