2008-10-16 7 views
9

En C#, quel est le meilleur moyen d'accéder à une propriété de la classe dérivée lorsque la liste générique contient uniquement la classe de base.Accès à une propriété de classe dérivée de la classe de base en C#

public class ClassA : BaseClass 
{ 
    public object PropertyA { get; set; } 
} 

public class ClassB: BaseClass 
{ 
    public object PropertyB { get; set; } 
} 

public class BaseClass 
{ 
} 

public void Main 
{ 
    List<BaseClass> MyList = new List<BaseClass>(); 
    ClassA a = new ClassA(); 
    ClassB b = new ClassB(); 

    MyList.Add(a); 
    MyList.Add(b); 

    for(int i = 0; i < MyList.Count; i++) 
    { 
     //I would like to access PropertyA abd PropertyB from the derived classes   
    } 
} 

Répondre

19

Certes, vous pouvez baissés, comme ceci:

for (int i = 0; i < MyList.Count; i++) 
{ 
    if (MyList[i] is ClassA) 
    { 
     var a = ((ClassA)MyList[i]).PropertyA; 
     // do stuff with a 
    } 

    if (MyList[i] is ClassB) 
    { 
     var b = ((ClassB)MyList[i]).PropertyB; 
     // do stuff with b 
    } 
} 

... Cependant, vous devriez prendre un autre regard sur ce que vous essayez d'accomplir. Si vous avez un code commun qui doit accéder aux propriétés ClassA et ClassB, il est préférable d'envelopper l'accès à ces propriétés dans une propriété ou une méthode virtuelle partagée dans la classe ancêtre.

Quelque chose comme:

public class BaseClass 
{ 
    public virtual void DoStuff() { } 
} 

public class ClassA : BaseClass 
{ 
    public object PropertyA { get; set; } 

    public override void DoStuff() 
    { 
     // do stuff with PropertyA 
    } 
} 

public class ClassB : BaseClass 
{ 
    public object PropertyB { get; set; } 

    public override void DoStuff() 
    { 
     // do stuff with PropertyB 
    } 
} 
+1

J'ai utilisé le deuxième exemple de code pour accéder à mes propriétés dans mes classes dérivées ...... Merci pour votre contribution. –

+1

C'est génial. J'ai cherché haut et bas et suis tombé sur de nombreux sujets qui disaient que c'était impossible, mais cette solution fonctionne à merveille. – Caustix

1

La prémisse entière n'a pas de sens - que serait la propriété B pour l'instance?

Vous pouvez le faire si vous effectuez une vérification manuelle du type d'exécution (inst est Foo), puis transtypez vers le type avec la propriété souhaitée.

1
BaseClass o = MyList[i]; 
    if (o is ClassB) 
    { 
     object k = ((ClassB)o).PropertyB; 
    } 
    if (o is ClassA)) 
    { 
     object j = ((ClassA)o).PropertyA; 
    } 
0

Vous pourriez avoir quelques problèmes avec Génériques et sous-classes (dans ce cas, vous devriez revenir à System.Collections.ArrayList), mais vous devez jeter les BaseClass à la sous-classe que vous souhaitez utiliser. Si vous utilisez le répertoire 'as', il réussira si la BaseClass peut être castée dans la sous-classe, ou elle sera nulle si elle ne peut pas être castée. Il ressemblerait à quelque chose comme:

for(int i = 0; i < MyList.Count; i++) 
{ 
    BaseClass bc = MyList[i]; 
    ClassA a = bc as ClassA; 
    ClassB b = bc as ClassB; 
    bc.BaseClassMethod(); 
    if (a != null) { 
     a.PropertyA; 
    } 
    if (b != null) { 
     b.PropertyB; 
    } 
} 

Aussi, je devrais mentionner que cela sent un peu mauvais. C'est le genre de code qui indique une hiérarchie d'objet mal structurée. En général, si vous ne pouvez pas dire IS A BaseClass, votre conception est probablement erronée. Mais, espérons que ça aide!

2

Si vous faites cela beaucoup, une autre option serait de créer une méthode d'extension sur la liste pour vous rendre l'énumération tapé correctement. à savoir

public static class MyBaseListExtensions 
    { 
    public static IEnumerable<ClassA> GetAllAs(this List<MyBaseClass> list) 
    { 
     foreach (var obj in list) 
     { 
     if (obj is ClassA) 
     { 
      yield return (ClassA)obj; 
     } 
     } 
    } 

    public static IEnumerable<ClassB> GetAllbs(this List<MyBaseClass> list) 
    { 
     foreach (var obj in list) 
     { 
     if (obj is ClassB) 
     { 
      yield return (ClassB)obj; 
     } 
     } 
    } 
    } 

Ensuite, vous pouvez l'utiliser comme ....

private void button1_Click(object sender, EventArgs e) 
{ 
    ClassA a1 = new ClassA() { PropertyA = "Tim" }; 
    ClassA a2 = new ClassA() { PropertyA = "Pip" }; 
    ClassB b1 = new ClassB() { PropertyB = "Alex" }; 
    ClassB b2 = new ClassB() { PropertyB = "Rachel" }; 

    List<MyBaseClass> list = new List<MyBaseClass>(); 
    list.Add(a1); 
    list.Add(a2); 
    list.Add(b1); 
    list.Add(b2); 

    foreach (var a in list.GetAllAs()) 
    { 
    listBox1.Items.Add(a.PropertyA); 
    } 

    foreach (var b in list.GetAllbs()) 
    { 
    listBox2.Items.Add(b.PropertyB); 
    } 
} 
0

Vous devez avoir les propriétés déclarées comme virtuel sur la classe de base, puis les remplacer dans la classe dérivée.

Ex:

public class ClassA : BaseClass 
{ 
    public override object PropertyA { get; set; } 
} 

public class ClassB: BaseClass 
{ 
    public override object PropertyB { get; set; } 
} 

public class BaseClass 
{ 
    public virtual object PropertyA { get; set; } 
    public virtual object PropertyB { get; set; } 
} 

public void Main 
{ 
    List<BaseClass> MyList = new List<BaseClass>(); 
    ClassA a = new ClassA(); 
    ClassB b = new ClassB(); 

    MyList.Add(a); 
    MyList.Add(b); 

    for(int i = 0; i < MyList.Count; i++) 
    { 
    // Do something here with the Property 
     MyList[i].PropertyA; 
     MyList[i].PropertyB;  
    } 
} 

Vous devez soit mettre en œuvre la propriété dans la classe de base pour renvoyer une valeur par défaut (comme nul) ou pour le rendre abstrait et forcer toutes les classes dérivées à mettre en œuvre les deux propriétés .

Vous devriez également noter que vous pouvez retourner des choses différentes pour dire PropertyA en le remplaçant dans les deux classes dérivées et en renvoyant des valeurs différentes.

5

Suite à la réponse de TimJ, vous pouvez écrire une méthode d'extension qui fonctionne pour tous les types:

public static IEnumerable<T> OfType<T>(this IEnumerable list) 
{ 
    foreach (var obj in list) 
    { 
     if (obj is T) 
      yield return (T)obj; 
    } 
} 

Ou si vous avez Linq, cette fonction est dans l'espace de noms System.Linq.

+0

J'aime cette solution aussi .... Merci pour l'information –

Questions connexes