Benutzer mit den meisten Antworten
Polymorphismus und Aufruf spezieller Methoden

Frage
-
Hallo NG,
ich habe 3 Klassen (sagen wir class BaseClass, Derived1 abgeleitet von BaseClass, Derived2
abgeleitet von BaseClass).
Objektbildung:
Je nach Programm-Situation werden die Objekte gebildet:
BaseClass baseObject = new Derived1();
oder eben
BaseClass baseObject = new Derived2();
Die virtuellen Methoden werden wie erwartet richtig aufgerufen, das passt soweit.
Problem:
Wie werden jetzt die speziellen Methoden von Derived1 oder Dervid2 aufgerufen?
Man kann doch nicht alle speziellen Methoden als virtuelle "leere" Huellen in die
Basisklassen aufnehmen, damit diese dann auch aufgerufen werden koennen.
Man kann natuerlich auch spezielle Objekte bilden (Derived1 der1 = new Derived1())
aber dann ist doch der Sinn von Polymorphismus wieder dahin.
Wie macht man es dann richtig?
Vielen Dank fuer Tipps und Vorschlaege.
Gruss, myamadeus
Antworten
-
Hallo myamadeus,
im wesentlichen ist das genau so. Ich möchte nur folgendes abschließend klären:
Upcasting und Downcasting sind OOP-Begriffe, die semantisch mit den Begriffen superclass (Basisklasse, in UML oben dargestellt) und subclass (abgeleitete Klasse, in UML unter der superclass dargestellt) zusammenhängen:
Beim Upcasten wird ein Objekt eines abgeleiteten Typs in ein Objekt seiner Basisklasse konvertiert.
BaseClasse bc = new Derived1();
Beim Downcasten (in C# immer ein code smell) wird ein Objekt der Basisklasse in ein abgeleitetes Objekt konvertiert:
// Läßt sich nicht kompilieren BaseClass bc = new BaseClass(); Derived1 d1 = (Derived1)((object)bc); // Dies geht, "stinkt" aber gewaltig BaseClass bc = new Derived1(); Derived1 d1 = (Derived1)bc;
Beim von dir angesprochenen Snippet:
BaseClass baseObjekt = new Derived1();
handelt es sich - in C#-Termini diesmal - um eine implizite Verweiskonvertierung (reference conversion).
Eine implizite Verweiskonvertierung ist immer erfolgreich und *verändert nicht die referentielle Identität* des konvertierten Objekts. Dies kann über Object.ReferenceEquals(bc, d1) überprüft werden. Nur der Verweistyp (Typ der Referenz) wird dabei geändert, so dass man nach erfolgter Verweiskonvertierung nur noch über die Schnittstelle des neuen Typs auf das Objekt zugreifen kann.
Gruß
Marcel
- Als Antwort vorgeschlagen Marcel RomaModerator Dienstag, 11. Juni 2013 08:34
- Als Antwort markiert Marcel RomaModerator Donnerstag, 13. Juni 2013 23:43
-
Hi,
ich meinte eine Lösung etwa so:using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { RezeptUnsercontrol1 c1 = new RezeptUnsercontrol1(); var res = c1.Execute<RezeptManager1>("Aufruf 1"); Console.WriteLine(res); Console.ReadKey(); } } public class UserControlBasis { public string Execute<T>(string par) { var c = this.GetInstance<T>(); return c.GetString(par); } public static RezeptManagerBasis manager { get; set; } } public static class Extensionclass { public static RezeptManagerBasis GetInstance<T>(this UserControlBasis uc) { return (RezeptManagerBasis)Activator.CreateInstance(typeof(T)); } } public class RezeptUnsercontrol1 : UserControlBasis { } public class RezeptUnsercontrol2 : UserControlBasis { } public class RezeptManagerBasis { public string GetString(string par) { return string.Format("Typ: {0} - {1}",this.GetType(), par); } } class RezeptManager1 : RezeptManagerBasis { } class RezeptManager2 : RezeptManagerBasis { } }
--
Viele Gruesse
Peter- Als Antwort vorgeschlagen Marcel RomaModerator Dienstag, 11. Juni 2013 08:34
- Als Antwort markiert Ionut DumaModerator Donnerstag, 13. Juni 2013 15:04
Alle Antworten
-
-
Hallo LittleBlueBird,
danke fuer den Link, aber das hilft mir nicht weiter. Die Theorie dazu ist mir bekannt, habe
auch schon etliche Buecher dazu gelesen.
Was ich suche betrifft mehr die Objektbildung zur Laufzeit, also wann nehme ich ein Objekt
der Basisklasse und wann das spezielle Objekt der Subklasse?
Verwende ich ein Objekt der Basisklasse als Upcast, dann kann ich Polymorphie einsetzen,
komme aber nicht an die speziellen Methoden der Subklasse ran.
Nehme ich ein spezielles Subclass Objekt, dann ist der Vorteil des Polymorphismus dahin.
Ich feue mich auf weitere Tipps.
Gruss, myamadeus
-
hi myamadeus,
Wieso soll der Polymorphismus hin sein?
du rufst die speziellen funktionen der Derived klassen auf.
DerivedObject.myfunc();
Wenn du in der Basisklasse bist kannst du natürlich nicht (zumindestens nicht sauber) auf die Abgeleiteten funktionen zugreifen.
Die Dirtylösung du castest this in der BaseClass auf DerivedClass....
Wenn du aber eine Basisfunktion in der Ableitung explizit aufrufen willst dann musst du base.myfunc() aufrufen...
Vielleicht konkretesierst du dein Problem anhand eines Beispieles dann sieht man leichter was du willst...
-
Hallo Brian Dahl,
>Wieso soll der Polymorphismus hin sein?
Weil ich ein Upcast Objekt habe, also sowas:
BaseClass baseObjekt = new Derived1();
Damit kannst du NICHT! die speziellen Methoden der abgeleiteten Klasse
aufrufen, wenn man nicht auf die Subklasse castet!
Ich will ja nicht hundert verschiedene Objekte erzeugen, halt eins mit dem
Basisverhalten.
Was ich braeuchte ist so ne Art Instance-Manager, der mir entweder ein BaseClass-Objekt oder
ein SubClass-Objekt erzeugt, je nach Programm-Situation.
>Die Dirtylösung du castest this in der BaseClass auf DerivedClass....
Du sagst es, ist wirklich ne Dirtyloesung.
>Vielleicht konkretesierst du dein Problem anhand eines Beispieles dann sieht man leichter was du
Ok ich probiers mal.
Es gibt 2 Rezept-Usercontrols, die von einer gemeinsamen Usercontrol-Basisklasse erben.
Diese Usercontrols rufen von einer Rezept-Assembly Methoden eines Rezept-Managers auf
(der besteht auch aus einer Basisklasse und 2 Spezialisierungen). Je nach Rezepttyp (respektive Usercongtrol) wird dann
ein Upcast-Objekt des REzept-Mangers erzeugt. Es gibt manchmal aber Programmsituationen,
da will man auf spezielle Methoden des Rezeptmanagers zugreifen. Das geht dann aber mit dem
Upcast-Objekt nicht mehr, man muesste dann ein spezielles SubClass-Objekt bilden und benutzen.
Das ist im Programmablauf aber stoerend, man will ja nicht immer Fallunterscheidungen haben,
ob es nun RezeptTyp1 oder RezeptTyp 2 ist.
Also wenn es jetzt nicht verstanden wird, muß ich das ganze Programm posten.
Gruß, myamadeus.
-
Hallo myamadeus,
Es ist wichtig zu verstehen, dass ein abgeleitetes Objekt, z.B. eine Instanz von Derived1, gleichzeitig eine Instanz der Basisklasse ist. Derived1 kapselt keine Instanz von BaseClass, Derived1 hat auch sonst keine "Beziehung" (über Referenzen, Pointer etc.) zu BaseClass, nein, Derived1 ist BaseClass (und gleichzeitig mehr).
Im Laufzeitobjekt von Derived1 werden also zuerst die Felder von BaseClass in der Reihenfolge ihrer Deklaration zu finden sein, dann die eigenen Felder. Zuerst werden diese Felder initialisiert, dann werden die Konstruktoren von BaseClass und DerivedClass in exakt dieser Reihenfolge aufgerufen. Um dir klar zu machen wie sehr Derived1 gleichzeitig BaseClass ist, ein kleines Experiment: Sollte die Basisklasse irgendwelche virtuellen Methoden im Basisklassenkonstruktor aufrufen (bad practice), dann werden die Overrides in Derived1 (!) aufgerufen noch bevor Derived1 zu Ende konstruiert ist:
class BaseClass { public BaseClass() { // Nicht nachmachen: bad practice Foo(); }
public virtual void Foo() { Console.WriteLine("BaseClass.Foo()"); } }
class Derived1 : BaseClass { public override void Foo() { Console.WriteLine("Derived1.Foo()"); } } // Test: Was wird im Konsolenfenster angezeigt? Derived1 d1 = new Derived1(); // Antwort: Derived1.Foo()Wenn Derived1.Foo() auf Initialisierung angewiesen ist, die im Derived1-Konstruktor ausgeführt wird, dann wurde die Methode oben noch vor der Initialisierung aufgerufen!
Es sollte bisher zumindest klar sein, dass BaseClass nicht außerhalb von Derived1 zu suchen ist, sondern ein Teil davon.
Wann nimmst Du die Basisklasse? - Dann wenn Du die Overrides und Neuimplementierungen der abgeleiteten Klasse nicht brauchst, sondern mit dem Grundstock an Funktionalität zurechtkommst, die die Basisklasse anbietet. Hast Du z.B. eine Methode, welche dir die Art zu der ein Tier gehört auf der Konsole ausgibt, brauchst Du nicht für jede Subklasse der Klasse Animal eine Ausgabemethode zu definieren, sondern einfach nur:
private void PrintSpecies(Animal animal) {
Console.WriteLine(animal.Species);
}Du kannst jetzt der Methode beliebige von Animal abgeleitete Instanzen übergeben: Monkey, Giraffe etc., denn jede dieser Laufzeitinstanzen ist ein Animal.
Im Gegenzug verwendest Du dann eine Ableitung, wenn die in der Basisklasse gekapselten Daten und Verhalten für die speziellen Zwecke unzureichend sind. Eine Monkey-Klasse könnte die Klasse z.B. Animal um die Methode Monkey.Climb() erweitern. Das ist eine Verhaltens-Spezialisierung, die wir weder von einem Animal noch von einer Giraffe erwarten.
Das Laufzeitobjekt, dass Du mit new Derived1() erstellst, behält auch dann den Laufzeittyp Derived1() wenn Du es einer BasisClass-Variablen zuweist!
Derived1 d1 = new Derived1(); BaseClass bc = d1; Console.WriteLine(bc.GetType()); // Gibt "ConsoleApplication1.Derived1" aus
Obwohl der Laufzeittyp der Variablen bc noch immer Derived1 ist, können wir nicht mehr auf die öffentlichen Member von Derived1 zugreifen, wir sind nun ganz auf die von BaseClass definierte engere Schnittstelle limitiert. Und zwar so sehr, dass wir bc nicht einfach wieder an d1 zurückzuweisen können. Möchten wir das, dann müssen wir eine explizite Konvertierung vornehmen:
Derived1 d2 = (Derived1) bc;
Polymorphismus kommt aus dem Griechischen und bedeutet "vielförmig". Unsere Klasse Derived1 ist vielförmig, hat ein Janus-Gesicht, kann mal als Basisklasse mal als Ableitung auftreten, denn sie ist beides.
Virtuelle Methoden der Basisklasse können über override in der Ableitung überschrieben werden, sie können in der abgeleiteten Klasse neu eingeführt (ausgeblendet) werden (über new), und zeigen so eine vielförmige, polymorphe Gestalt in der Ableitung. Selbst wenn der Laufzeittyp der Basisklassenvariablen bc im obigen Beispiel noch immer Derived1 ist, können wir die Vorteile des Polymorphismus nicht mehr nutzen, da wir die Funktionalität von Derived1 auf die der Basisklasse ganz bewußt eingeschränkt haben.
P.S. Niemand hindert dich:
aufzurufen. Du begibst dich aber damit auf einem gefährlichen Terrain, wo Du nicht nur Intellisense verlierst, sondern auch einen guten Teil der Typsicherheit für die wir alle C# mögen.dynamic d = bc;
d.[OverridenMethod]();Gruß
Marcel- Bearbeitet Marcel RomaModerator Donnerstag, 6. Juni 2013 10:48 P.S.
-
Hallo Marcel,
vielen Dank fuer die ausfuehrliche Antwort.
Ist alles richtig und schoen erklaert, ich muss es erstmal alles verarbeiten :-)
Ok, was ich fuer mich jetzt (auch Dank Deiner Hilfe) als logisch erklaert:
1. Komme ich mit der Funktionalitaet der Basisklasse aus, dann langt ein
Upcast-Objekt (verdammt wie heisst das Ding eigentlich richtig?).
Also:
BaseClass baseObjekt = new Derived1() oder new Derived2().
Damit nutze ich die Funktionalitaet der Basisklasse inklusive der Vorteile der virtuellen Funktionen
die (evtl) von der Subklasse ueberschrieben wurden.
2. Brauche ich aber unbedingt die Methoden der Spezialisierungen, dann fuert kein Weg dran vorbei und ich muss ein NEUES! Objekt der Subklasse erzeugen oder das vorhandene auf
den Spezialtyp casten:
Derived1 derived1 = new Derived1();
oder
((Derived1)baseObject).SpecialDerivedMethod().....
Ich hoffe, ich habe es so richtig verstanden.
Danke an alle!
Gruss, mymadeus.
-
Hallo myamadeus,
im wesentlichen ist das genau so. Ich möchte nur folgendes abschließend klären:
Upcasting und Downcasting sind OOP-Begriffe, die semantisch mit den Begriffen superclass (Basisklasse, in UML oben dargestellt) und subclass (abgeleitete Klasse, in UML unter der superclass dargestellt) zusammenhängen:
Beim Upcasten wird ein Objekt eines abgeleiteten Typs in ein Objekt seiner Basisklasse konvertiert.
BaseClasse bc = new Derived1();
Beim Downcasten (in C# immer ein code smell) wird ein Objekt der Basisklasse in ein abgeleitetes Objekt konvertiert:
// Läßt sich nicht kompilieren BaseClass bc = new BaseClass(); Derived1 d1 = (Derived1)((object)bc); // Dies geht, "stinkt" aber gewaltig BaseClass bc = new Derived1(); Derived1 d1 = (Derived1)bc;
Beim von dir angesprochenen Snippet:
BaseClass baseObjekt = new Derived1();
handelt es sich - in C#-Termini diesmal - um eine implizite Verweiskonvertierung (reference conversion).
Eine implizite Verweiskonvertierung ist immer erfolgreich und *verändert nicht die referentielle Identität* des konvertierten Objekts. Dies kann über Object.ReferenceEquals(bc, d1) überprüft werden. Nur der Verweistyp (Typ der Referenz) wird dabei geändert, so dass man nach erfolgter Verweiskonvertierung nur noch über die Schnittstelle des neuen Typs auf das Objekt zugreifen kann.
Gruß
Marcel
- Als Antwort vorgeschlagen Marcel RomaModerator Dienstag, 11. Juni 2013 08:34
- Als Antwort markiert Marcel RomaModerator Donnerstag, 13. Juni 2013 23:43
-
Hallo Peter,
sorry, hatte Dich uebersehen.
Du meinst sowas?:
object oMyObject = new Derived1(); Type t = typeof(Derived1); MethodInfo testMethod = t.GetMethod("Test"); testMethod.Invoke(oMyObject, null);
Ich denke, dass muesste auch klappen.
Danke fuer den Hinweis.
Gruss, myamadeus.
-
Hi,
ich meinte eine Lösung etwa so:using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { RezeptUnsercontrol1 c1 = new RezeptUnsercontrol1(); var res = c1.Execute<RezeptManager1>("Aufruf 1"); Console.WriteLine(res); Console.ReadKey(); } } public class UserControlBasis { public string Execute<T>(string par) { var c = this.GetInstance<T>(); return c.GetString(par); } public static RezeptManagerBasis manager { get; set; } } public static class Extensionclass { public static RezeptManagerBasis GetInstance<T>(this UserControlBasis uc) { return (RezeptManagerBasis)Activator.CreateInstance(typeof(T)); } } public class RezeptUnsercontrol1 : UserControlBasis { } public class RezeptUnsercontrol2 : UserControlBasis { } public class RezeptManagerBasis { public string GetString(string par) { return string.Format("Typ: {0} - {1}",this.GetType(), par); } } class RezeptManager1 : RezeptManagerBasis { } class RezeptManager2 : RezeptManagerBasis { } }
--
Viele Gruesse
Peter- Als Antwort vorgeschlagen Marcel RomaModerator Dienstag, 11. Juni 2013 08:34
- Als Antwort markiert Ionut DumaModerator Donnerstag, 13. Juni 2013 15:04