Laden...

List mit Subklassen einer Basisklasse - Zugriff auf Subklassen Eigenschafen und Methoden

Erstellt von AlienWorkshop vor 8 Jahren Letzter Beitrag vor 8 Jahren 4.987 Views
A
AlienWorkshop Themenstarter:in
6 Beiträge seit 2015
vor 8 Jahren
List mit Subklassen einer Basisklasse - Zugriff auf Subklassen Eigenschafen und Methoden

Hi Zusammen,

benötige eure Hilfe. Es ist leider auch nicht einfach das Problem zu beschreiben weshalb der Titel vll falsch ist. Danach zu suchen ist auch nicht einfach da ich nicht genau weis nach was ich suchen muss.

Ich versuche es mal zu beschreiben. Ich möchte gerne Subklassen einer Hauptklasse in einer Liste speichern und später wieder darauf zugreifen.

Hier mal ein Beispielkonstrukt


         public class Container
        {
            public List<A> aInstances;
        }

        private abstract class A
        {
            public int type;
        }

        public class A1 : A
        {
            public int A1Value;

            public void methodA1()
            {
                // Do Some Stuff
            }
        }

        public class A2 : A
        {
            public int A2Value;
            public void methodA2()
            {
                // Do Some other Stuff than A1
            }
        }

Und so könnte der Zugriff aussehen. Problem ist hier das die liste vom Type A ist und man somit bei den Eigenschafen und Methoden auf die Basisklasse beschränkt ist


private void Button1_Click(object sender, EventArgs e)
        {
            Container cont = new Container();
            cont.aInstances = new List<A>();
            cont.aInstances.Add(new A1());

            //Dies hier wäre also nicht möglich da die Methode methodA1 in der Basisklasse nicht existiert
            cont.aInstances[0].methodA1();

            //geht ebenfalls nicht ohne einen Cast
            A1 a1 = cont.aInstances[0];
        }

Gibt es eine bessere Variante diesen Aufbau zu implementieren? Ich möchte gerne Casts, Objects und Dynamics aussen vorlassen.

3.003 Beiträge seit 2006
vor 8 Jahren

foreach(var a1class in container.aInstances.OfType<A1>)
   a1class.methodA1();

Im übrigen eventuell die empfohlenen Namenskonventionen für c# beachten, es hilft beim Lesen.

LaTino
EDIT: wenn sich die Methoden eine Schnittstelle teilen, und du sie (mit Hilfe der Liste) nur als Schnittstelle ansprichst, dann kannst du sie nur per unboxing (also casting) als konkrete Objekte ansprechen. Das ist ein OOP-Kernelement.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

3.003 Beiträge seit 2006
vor 8 Jahren

Okay, eine Möglichkeit ohne Cast, allerdings hinten rum und dann durchs Auge:


   interface IBaseInterface
    {
        string Name { get; set; }
        void CommonMethod();
    }


    class SubClassA : IBaseInterface
    {
        public string Name { get; set; }
        public void CommonMethod() { Console.WriteLine("SubClassA.CommonMethod"); }

        public void SpecialMethodA()
        {
            Console.WriteLine("SpecialMethod A");
        }
    }

    class SubClassB : IBaseInterface
    {
        public string Name { get; set; }
        public void CommonMethod() { Console.WriteLine("SubClassB.CommonMethod"); }

        public void SpecialMethodB()
        {
            Console.WriteLine("SpecialMethod B");
        }
    }

    class AdapterClass : IBaseInterface
    {
        private IBaseInterface _wrappedClass;
        public delegate void SpecialMethodDelegate();

        public SpecialMethodDelegate SpecialMethod { get; set; }

        public AdapterClass(SubClassA wrappedClass)  
        {
            _wrappedClass = wrappedClass;
            SpecialMethod = wrappedClass.SpecialMethodA;
        }

        public AdapterClass(SubClassB wrappedClass)
        {
            _wrappedClass = wrappedClass;
            SpecialMethod = wrappedClass.SpecialMethodB;
        }

        public string Name
        {
            get { return _wrappedClass.Name; }
            set { _wrappedClass.Name = value; }
        }

        public void CommonMethod()
        {
            _wrappedClass.CommonMethod();
        }
    }

Anwendung:


   public class Client
    {
        public void DoSomething()
        {
            var myList = new List<AdapterClass>();
            myList.Add(new AdapterClass(new SubClassA()));
            myList.Add(new AdapterClass(new SubClassB()));

            foreach (var item in myList)
                item.SpecialMethod();
        }
    }

Ich wüsste aber nicht, wo das praktikabler wäre als ein schmerzloser Cast.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

W
955 Beiträge seit 2010
vor 8 Jahren

Kannst du nicht einfach die Funktionalität in den Subklassen abbilden, also dass A1 und A2 eine methodA besitzen? Dort machst du dann den Code von methodA1 bzw. methodA2 rein. Also der Client ruft einfach methodA auf und die Objekte wissen dann selber was sie tun müssen.

3.003 Beiträge seit 2006
vor 8 Jahren

@witte, das wäre auch die einzige Anwendungsweise, wo das Konstrukt Sinn ergibt. Meine Vermutung war, dass die konkreten Objekte wesentlich komplexer sind und eben einige Methoden haben, die polymorphisch sind, und dass es mit MethodeA1 und MethodeA2 nur um einen Sonderfall geht.

Ansonsten wäre das natürlich schon irgendwie traurig 😉.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

446 Beiträge seit 2004
vor 8 Jahren

Eine Wrapper Klasse brauchst du nicht um abgeleitete Klassen in eine Liste zu stecken, weil das IEnumberable Interface für T Kovaraint ist.


using System;
using System.Collections.Generic;

public class Program
{
	public static void Main()
	{
		List<Abase> basetypelist = new List<Abase>();
		basetypelist.Add(new B());
		basetypelist.Add(new C());
		foreach (var a in basetypelist)
		{
			if (a.GetType().Equals(typeof (C)))
			{
				C c = (a as C);
				Console.WriteLine(a.GetType() + c.foo());
			}
			else
			{
				Console.WriteLine(a.GetType());
			}
		}
	}
}

public abstract class Abase
{
}

public class B : Abase
{
}

public class C : B
{
	public String foo()
	{
		return "foo";
	}
}

https://dotnetfiddle.net/aT98ZI

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

3.003 Beiträge seit 2006
vor 8 Jahren

@Briefkasten, das ist nicht das Problem, um das es geht.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

446 Beiträge seit 2004
vor 8 Jahren

@Briefkasten, das ist nicht das Problem, um das es geht.

LaTino

Keine Casts - überlesen.

[Edit]
Ohne Casts wirst du aber nicht drum herum kommen - mit allem anderen bist du wieder zu sehr gebunden und unflexibel.

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

3.003 Beiträge seit 2006
vor 8 Jahren

Mein Beispiel oben kommt ohne Casts aus, benutzt aber auch kein auto-boxing. Wenn man in eine generische Liste verschiedene konkrete Objekte reinpappt, muss man eben casten, wenn man wieder an ein konkretes Objekt heran will. Optimal wäre, wie witte schrieb, ein sauberes Interface.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

C
2.121 Beiträge seit 2010
vor 8 Jahren

Ich würde mich zuallererst fragen warum Casts ausgeschlossen werden sollen. Die gibt es für solche Anwendungsfälle, also wüsste ich nicht warum ich mir das Leben schwer machen sollte und auf sie verzichte.
Ausufernd sollte das nicht werden, falls doch ist vielleicht noch eine Verbesserung der Programmstruktur machbar.
Oder eine Methode in der Basisklasse die nur in der gewollten Subklasse etwas tut.

A
AlienWorkshop Themenstarter:in
6 Beiträge seit 2015
vor 8 Jahren

Danke euch allen für die detailierten Antworten und Erklärungen.

Sorry wenn nicht alles 100% namenskonform und auch sonst nicht perfekt ist, es ist prinzipiell alles nur hobbiemässig angeeignetes Wissen, bin aber natürlich froh um eure Hinweise.

Die angedeutete Strukturen sind natürlich nur beispiele und die eigentlichen Klassen haben neben unterschliedlichen Methoden auch unterschiedliche Eigenschafen und Variablen.

Es ist auch nicht möglich funktionen in allen Subklassen gleich zu Defineren da diese eben Aufgrund der unterschiedlichen Eigenschaften und Variablen teilweise auch andere Parameter benötigen und/oder grundlegend andere Aufgaben durchführen.

Soweit ich mich mit C# beschäftigt habe ist es eher "Last Restort" Casts, Objects oder Dynamics zu verwenden, weshalb ich diese möglichkeiten auslassen wollte. Deshalb auch die Frage an euch ob dies möglich ist.

Anscheinend komme ich wohl nicht um das verwenden der Casts herum.

Werde also mit typeof() in einer select pro list item arbeiten müssen um dann entsprechende casts durchzuführen damit ich auf die Subklassen Eigenschaften zugreifen kann.

Danke euch vielmals.

C
2.121 Beiträge seit 2010
vor 8 Jahren

Soweit ich mich mit C# beschäftigt habe ist es eher "Last Restort" Casts, Objects oder Dynamics zu verwenden, weshalb ich diese möglichkeiten auslassen wollte.

Ich würde mir da keine Gedanken machen. Sollen andere zeilenweise Umwege programmieren nur um irgendwas nicht zu verwenden. Wenn etwas elegant funktioniert nutze ich es, egal wie "in" es gerade ist 😉

Wenn du wirklich verschiedene Methoden oder Eigenschaften, dann bleibt dir nichts anderes übrig als alles einzeln abzuprüfen. Wenns nicht anders geht und sich die Anzahl in Grenzen hält, finde ich das auch gar nicht so schlimm.

3.003 Beiträge seit 2006
vor 8 Jahren

Die angedeutete Strukturen sind natürlich nur beispiele und die eigentlichen Klassen haben neben unterschliedlichen Methoden auch unterschiedliche Eigenschafen und Variablen.

Es ist auch nicht möglich funktionen in allen Subklassen gleich zu Defineren da diese eben Aufgrund der unterschiedlichen Eigenschaften und Variablen teilweise auch andere Parameter benötigen und/oder grundlegend andere Aufgaben durchführen.

Wenn die Klassen so verschieden ausfallen, sich aber trotzdem eine Schnittstelle/Superklasse teilen, dann ist vermutlich dein Klassendesign für eine Überholung fällig. Ein Hinweis darauf ist schon, wenn man gegen eine konkrete Implementierung programmieren muss. Du solltest also etwas früher ansetzen - nicht bei der Implementierung, sondern eben beim Entwurf.

Soweit ich mich mit C# beschäftigt habe ist es eher "Last Restort" Casts, Objects oder Dynamics zu verwenden, weshalb ich diese möglichkeiten auslassen wollte. Deshalb auch die Frage an euch ob dies möglich ist.

Boxing/Unboxing ist zentrales Bestandteil jeder OOP. Insofern wäre ich sehr misstrauisch, wenn mir jemand erzählen möchte, dass z.B. Casts nur ein letzter Ausweg sind.

Werde also mit typeof() in einer select pro list item arbeiten müssen um dann entsprechende casts durchzuführen damit ich auf die Subklassen Eigenschaften zugreifen kann.

Nein, wirst du nicht.


myList.OfType<MyType>

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

T
461 Beiträge seit 2013
vor 8 Jahren

Haha, also das mit dem OfType<A> ist mir bisher nie so aufgefallen, danke für die Info. Manchmal könnte man sich danach im Hintern beißen, weil man erst später draufgekommen ist... wie ich gerade...

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

3.003 Beiträge seit 2006
vor 8 Jahren

Haha, also das mit dem OfType<A> ist mir bisher nie so aufgefallen, danke für die Info. Manchmal könnte man sich danach im Hintern beißen, weil man erst später draufgekommen ist... wie ich gerade...

Mir auch erst, seit ich vor einigen Jahren begann, Resharper einzusetzen (kann ich sowieso uneingeschränkt empfehlen). Der schlägt das immer mal vor. Selbe Richtung und auch enorm praktisch: Enumerable.Cast<TResult>-Methode

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

A
AlienWorkshop Themenstarter:in
6 Beiträge seit 2015
vor 8 Jahren

Nein, wirst du nicht.

  
myList.OfType<MyType>  
  

Da die Liste, Elemente der verschiedenen Subklassen hat, weis ich also vorher garnicht von welchem Typ welches list item ist, es ist ja nicht definiert sondern dynamisch. Ich muss also die liste iterieren und pro typ entsprechend dann weitermachen.

Edit: damit meine ich die Liste kann aber muss nicht Items verschiedener oder eines bestimmten besitzten.

1.029 Beiträge seit 2010
vor 8 Jahren

Hi,

genau darum ging es doch 😕

Das .OfType<X> gibt dir ja nur Elemente, die dem angefragten Typ entsprechen.

class A
	{

	}

	class B : A
	{

	}

	class C : A
	{

	}

	class Program
	{
		static void Main(string[] args)
		{
			TestList();
		}

		static void TestList()
		{
			List<object> list = new List<object>();
			list.Add(new B());
			list.Add(new C());

			Console.WriteLine(list.OfType<B>().Count());
			Console.ReadLine();
		}

LG

A
AlienWorkshop Themenstarter:in
6 Beiträge seit 2015
vor 8 Jahren

Hi,

den Typ weis ich ja aber vorher nicht 😉, ich möchte ja nicht einen bestimmten typ haben sondern durch alle listenitems iterieren, egal welchen Typ diese haben und dann aufgrund welcher typ es ist, anders reagieren.

16.807 Beiträge seit 2008
vor 8 Jahren

Das klingt irgendwie nicht durchdacht. Was ist denn der genaue Zweck, den Du vor hast. Vielleicht können wir Dir was besseres zB. einen geeigneten Pattern anbieten.
Du weisst, was Interfaces sind und wie man sie verwendet?

3.003 Beiträge seit 2006
vor 8 Jahren

Hi,

den Typ weis ich ja aber vorher nicht 😉, ich möchte ja nicht einen bestimmten typ haben sondern durch alle listenitems iterieren, egal welchen Typ diese haben und dann aufgrund welcher typ es ist, anders reagieren.

Du darfst ruhig ein bisschen mitdenken. Oberste Prio hätte natürlich, deine Klassenstruktur zu überarbeiten, aber folgende Zeilen sind nicht so schwer, dass du nicht selbst haettest drauf kommen können:


myList.OfType<Klasse1>.ForEach(p => p.SpecialMethod1());
myList.OfType<Klasse2>.ForEach(p => p.SpecialMethod2());

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

A
AlienWorkshop Themenstarter:in
6 Beiträge seit 2015
vor 8 Jahren

Interfaces sind mir bekannt lassen sich aber nicht anwenden in meinem Falle.(zumindest meiner Meinung nach nicht 😃 )

Ich versuche mal es zu erklären.

Ich bin als Network Engineer tätig und möchte gerne zur Arbeitserleichterung standard configs per tool erstellen können, damit die Standards des Kunden in sachen konfiguration eingehalten werden und keine Fehler bei z.b. der ebenfalls standardisierten Benamung der Konfigurationsitems stattfinden.

Konkret handelt es sich um LoadBalancer Konfigurationen. Ein Teil dieser Konfigurationen enhält sog. Answer welche Probes, Keepalives oder HealthChecks jenachdem wie man diese nennen möchte oder leichter versteht, enthalten. (Kurz gesagt überprüfen diese Probes die verfügbarkeit eines Services auf einem z.b. Server)

Diese Probes können von verschiedenen Typen sein z.b. ICMP (Ping), TCP, HTTP Head, HTTPS Heads und andere.

Die Konfiguration kann nun zu einem Element(IP Adresse normalerweise) auf das eine Lastverteilung stattfinden soll, mehrere oder keine dieser probes anwenden, sprich es kann sowohl sein das es keine Probe gibt. Es kann also gut sein das sowohl per ICMP als auch per TCP der HealthCheck auf dieses Element durchgeführt wird.

In dem GUI zu diesem Tool soll man sich entsprechen dies zusammenklicken können. Also das Grundelement anlegen, und dann eine,mehrere oder keine Probes konfigurieren.

So sehen z.b. die Klassen für diesen Teil der Konfig aus aus die ich bereits angelegt habe. Hieran sieht man das diese teilweise überlappende variablen haben aber eben auch unterschiedliche

Nochmal folgendes Kontrukt

Konfiguration
-> enhält Liste<Answer>
-> Answers können von Typ VIP, CRA oder NameServer sein

Answer Type VIP
-> enhält Liste<AnswerKeepaliveVipProbe>
-> Probes können vom Typ ICMP, TCP, HTTP oder HTTPS sein

Anser Type CRA und NameServer sind noch nicht ausgearbeitet


class GSS
    {
// Diese Liste enhält alle Answers eine configuration
private List<Answer> answers;

//  Alle Answers haben diese eigenschaften/Settings gemeinsam also kommen 
// diese in der Hauptklasse der Answers
public abstract class Answer
        {
            protected AnswerType type;
            protected String name;
            protected Location location;
            public bool ManualReactivation { get; set; }
            public bool Active { get; set; }
        }

// 3 Verschiende Arten an Answer gibt es  VIP, CRA und NameServer
// hier also die 3 Klassen entsprechend
public class AnswerVip : Answer
        {
            public String VIP { get; set; }
            private List<AnswerKeepaliveVipProbe> probes;
        }

public class AnswerKeepaliveCra : Answer
        {
            // Not yet implemented
        }

        public class AnswerKeepaliveNameServer : Answer
        {
            // Not yet implemented
        }


// Dummy um die verschiedenen arten an Probes 
// in einer Liste ablegen zu können
 public abstract class AnswerKeepaliveVipProbe 
        { 
            protected AnswerVipKeepaliveType type;
        }

public class AnswerKeepaliveVipProbeICMP : AnswerKeepaliveVipProbe
        {
            private bool UseVIP { get; set; }
            private String IPaddress;
            private AnswerVipSharedKeepalive SharedKeepalive;
        }

        public class AnswerKeepaliveVipProbeTCP : AnswerKeepaliveVipProbe
        {
            public bool UseVIP { get; set; }
            public String IPaddress;
            public String DestPort;
            public ConnectionTerminationMethod ConnMethod;
            public AnswerVipSharedKeepalive SharedKeepalive;
        }

        public class AnswerKeepaliveVipProbeHTTP : AnswerKeepaliveVipProbe
        {
            public AnswerVipKeepaliveType Type = AnswerVipKeepaliveType.http_head;
            public bool UseVIP { get; set; }
            public String HostTag;
            public String IPaddress;
            public String DestPort;
            public String Path;
            public ConnectionTerminationMethod ConnMethod;
            public AnswerVipSharedKeepalive SharedKeepalive;
        }

        public class AnswerKeepaliveVipProbeHTTPS : AnswerKeepaliveVipProbe
        {
            public AnswerVipKeepaliveType Type = AnswerVipKeepaliveType.https_head;
            public bool UseVIP { get; set; }
            public String HostTag;
            public String IPaddress;
            public String DestPort;
            public String Path;
            public SSLVersion SSlVers;
            public AnswerVipSharedKeepalive SharedKeepalive;
        }

}

Wie würdet Ihr so ein Konstrukt abbilden? Danke schonmal für eure Hilfe.

Hoffe ich konnte das einigermassen erklären.

A
AlienWorkshop Themenstarter:in
6 Beiträge seit 2015
vor 8 Jahren

Du kannst ruhig auch ein bisschen mitdenken 😉 vielleicht ist es nicht klar geworden aber es ist nicht das problem das ich nicht weis wie ich darauf zugreifen kann, es war nur die frage ob es eine elegantere methode gibt also überall diese CASTS verwenden zu müssen. Und ob ich nun über OfType mit der Liste arbeite oder alle items iteriere und mit select auf den Type Frage ist ja wohl egal, man merkt aber das die OfType funktion der List classe dir super gefällt 😉

Hinweis von Abt vor 8 Jahren

Bitte keine Fullquotes mehr

3.003 Beiträge seit 2006
vor 8 Jahren

Die simpelste Variante wäre, die Methoden/Properties (ich hoffe jedenfalls, dass du nicht einfach Member public machst in deinem Code) für alle Klassen verfügbar zu machen und in einer abstrakten Superklasse ein Standardverhalten (template method) zu definieren. Klassen, die die betreffende Eigenschaft dann haben, bieten dann eben eine eigene Überschreibung der Standardmethode.

Der Client dann (also das Programm, das die Klassen benutzt, um bspw. eine GUI zu erstellen) ignoriert einfach die Standard-Implementierungen, und fertig. Sehr unsauberes Beispiel, aber extra gemacht, damit klar wird, was ich meine:


abstract class MySuperClass
{
    protected const string DEFAULTSTRING = string.empty;
    public static string DefaultStringReply { get { return DEFAULTSTRING; } }


    public virtual string SpecialMethod1() { return DEFAULTSTRING; }
    public virtual string SpecialMethod2() { return DEFAULTSTRING; }
}

class MyClass1 : MySuperClass 
{
    public override string SpecialMethod1() { return "Hello,"; }
}
class MyClass2 : MySuperClass 
{
   public override string SpecialMethod2() { return " world"; }
}

//client
var myList = GetSuperClassList();
foreach(var mySuperClassInstance in myList)
{
    var reply1 = mySuperClassInstance.SpecialMethod1();
    var reply2 = mySuperClassInstance.SpecialMethod2();
    if(reply1 != MySuperClass.DefaultStringReply) Console.Write(reply1);
    if(reply2 != MySuperClass.DefaultStringReply) Console.Write(reply2);
}

Wäre eine Möglichkeit. Deine Beschreibung des Szenarios klingt mir irgendwie nach builder pattern, schau dir das mal an.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)