PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Aufruf des Vorvorfahrens möglich?


Kabelsalat
2006-05-31, 11:54:08
Hallo,

Ich stehe gerade vor einem Problem, was ich in dieser Art noch nie hatte, und somit habe ich mich auch noch nie damit beschäftigt:

Gegeben sind drei Klassen ClassA, ClassB und ClassC, welche in der Reihenfolge ihrerer Bennenung von einander erben. ClassA implementiert eine als virtuell deklarierte Funktion DoSth. Diese wird in den beiden erbenden Klassen überschrieben. Nun ruft ClassB zwar wie notwendig die Basis-Funktion aus ClassA auf führt aber auch noch eigenen, unerwünschten Code aus - Aus der in ClassC ebenfalls überschriebenen Funktion muss ich jedoch wieder die ursprüngliche Basis aus ClassA rufen - wie stelle ich das am blödsten an? base.base (C#) wird es ja wohl kaum geben... Ein Casten auf den Typ von ClassA natürlich auch nichts, da die Funktion nicht mit new überschrieben wurde und somit ein ((ClassA)this).DoSth() immer wie this.DoSth() wirken würde.

Das Problem bezieht sich zwar auf C#, müsste aber auf alle Sprachen etwa gleich zutreffen. Antworten bezüglich jeglicher euch bekannter Sprachen sind somit hilfreich.

Danke für die Hilfe

Kabelsalat

del_4901
2006-05-31, 13:05:00
Du solltest die Methoden nicht als virtuell deklarieren, wenn B::doSth() nicht aufgerufen werden soll. Wenn du eh castest brauchst du keine virtuellen Methoden. vllt sollte B auch nicht von A erben.
Ich habe den Verdacht das da im Design schon nicht ganz zuende gedacht wurde.

wenn du in virtuellen Methodenaufrufen code in einer Oberklasse ausfürhen willst, braucht du eine zusätzliche non-virtuelle Methode.


class A
{
virtual DoSth(){doThis();}
doThis(){std::cout << "blaA";
}

class C : public A
{
DoSth(){A::doThis(); C::doThis();}
doThis(){std::cout << "blaC";
}

del_4901
2006-05-31, 13:08:51
Ohne die Methode gehts auch mit A::doSth();

hatte nur vergessen mein Projekt zu cleanen ^^

Kabelsalat
2006-05-31, 13:09:33
ClassA und ClassB sind Bestandteil des .Net-Frameworks und können somit nicht geändert werden. Dass sie als virtual deklariert sind hat im tatsächlichen Context auch seine Berechtigung...

del_4901
2006-05-31, 13:11:28
Kabelsalat[/POST]']ClassA und ClassB sind Bestandteil des .Net-Frameworks und können somit nicht geändert werden. Dass sie als virtual deklariert sind hat im tatsächlichen Context auch seine Berechtigung...
also mit C++ gehts. Wie oben genannt.
Bei java gehts mit super.doSth();

bei C# kein plan .. ^^

Du kannst ja von den Klassen erben und neue Methoden hinzufügen bzw. überschreiben. bei den virtuellen ist das etwas knifflig, aber mit oben genannten sicher möglich. .. nur ohne Beispiel ist das alles ein wenig doof ^^

Monger
2006-05-31, 13:18:58
So wie ich das sehe, ist das eigentlich ein Designfehler. Man erbt nunmal nur von EINER bestimmten Klasse - in diesem Fall KlasseB. Dass KlasseB wiederum von KlasseA erbt, ist ja in erster Linie mal egal. Wichtig ist, dass eine Klasse nicht über mehrere Ecken wissen kann (und darf), wie sie eigentlich entstanden ist.

Gefühlsmäßig würde ich sagen, dass in deinem Fall sowohl KlasseB als auch KlasseC von KlasseA direkt erben müssten. Wenn die sich irgendeine gemeinsame Eigenschaft teilen sollen, solltest du darüber nachdenken ob nicht ein Interface die richtige Wahl wäre.

Kabelsalat
2006-05-31, 13:19:24
Bei dir erbt ClassC aber auch direkt von ClassA.

Bei mir sieht die Situation so aus:


public ClassA //Bestandteil .Net-Framework
{
protected virtual <Type> DoSth() { ... }
}

public ClassB : ClassA //ebenfalls Bestandteil des .Net-Frameworks
{
protected override <Type> DoSth()
{
<Type> Var = base.DoSth();

//...
}
}

public ClassC : ClassB //meine Klasse
{
protected override <Type> DoSth()
{
<Type> Var = null; //Aufruf von DoSth von ClassA (wie?)

//...
}
}



Wenn möglich sollte ich die Funktion, welche ClassA zur Verfügung stellt aufrufen - bloß wie? Überhaupt möglich?


/edit:

Um es noch konkreter zu machen, der tatsächliche Aufbau:

Ich erbe von System.Web.UI.WebControls.Menu, welches in mehreren Schritten wiederum von System.Web.UI.Control abgeleitet ist. Ich stelle damit ein angepasstes Menü zur Verfügung - zur Runtime funktioniert das ganze auch wunderbar, allerdings sind ebenfalls Anpassungen notwendig, dass das Control im Visual Studio Web Forms Designer korrekt angezeigt wird. Aus diesem Grund muss ich IDictionary GetDesignTimeData() überschreiben. Nun wird diese Funktion ursprünglich von Control eingeführt und von Menu überschrieben. Ich sollte nun (wenn möglich - nicht unbedingt notwendig) zunächst GetDesignTimeData aus Control aufrufen und dann meine eigenen Elemente zu dem IDictionary-Objekt hinzufügen...

del_4901
2006-05-31, 13:29:05
probier mal base.base.doSth() ^^

bzw. wozu brauchst du überhaupt B? vllt solltest du eine Klasse als indirektion dazwischenschieben (wenn da irgendwas gemacht werden muss)
aber in dieser doSth() nicht überschreiben. B und C erben dann hiervon.

ne leider k.A ich weiß das es in C++ mit Klassenname::Methode geht ... geht das nicht auch in C#?

Kabelsalat
2006-05-31, 13:36:25
ClassB ist die Asp.Net Menu-Komponete. Deren Funktionalität will ich beibehalten, bloß die HTML-Ausgabe wird durch überschreiben der Render-Methoden angepasst - ClassB ist also unter allen Umständen notwendig, wenn ich nicht die ganze Logik, die hinter dem Menü steckt neu implementieren will. Bloß was die Ausgabe zur Design-Time angeht, habe ich ein klein wenig Probleme. Ich habe mir die Funktion der "Vorvorgänger"-Methode jedoch noch einmal näher angeschaut und es sieht so aus, als könne ich auf den Aufruf dieser verzichten. Damit bliebe noch der theoretische Rahmen - ist es nun möglich oder nicht? Die erwähnte C++-Syntax lässt sich nicht übertragen (zumindest nicht soweit ich es beurteilen kann. Wenn es eine Enstprechung in Java gibt, müsste es auch unter .Net funktionieren).

Die von der geschi

Gast
2006-05-31, 13:43:02
Könntest du nicht einfach die Methode aus A in B kapseln und dann von C aus aufrufen?

Kabelsalat
2006-05-31, 14:10:56
Betrachte A und B als vorgegeben und unabänderlich...

Alf@Uni
2006-05-31, 15:52:46
in Java soll Klassenname.methode() gehn.

Alf@Uni
2006-05-31, 15:54:17
Gast[/POST]']Könntest du nicht einfach die Methode aus A in B kapseln und dann von C aus aufrufen?

Ja das kenn er indem er ein derivat von B erstellt, und sich dort eine neue virtuelle Methode definiert.. dann kann er die anderen Methoden alle überschreiben

Trap
2006-05-31, 16:38:31
Es geht auf jeden Fall, die Frage ist nur wie hässlich es am Ende wird.

Die dümmste Methode die mir einfällt wäre per Reflection eine neue Methode in C zu erstellen die eine Kopie der virtuellen Methode in A ist (einfach Bytecodes kopieren) und die dann aufzurufen.

Kabelsalat
2006-05-31, 17:05:20
Alf@Uni[/POST]']in Java soll Klassenname.methode() gehn.

So werden doch auch in Java lediglich statische Member aufgerufen...?

Ja das kenn er indem er ein derivat von B erstellt, und sich dort eine neue virtuelle Methode definiert.. dann kann er die anderen Methoden alle überschreiben

Kannst du das evtl. nochmal etwas genauer erklären...

Es geht auf jeden Fall, die Frage ist nur wie hässlich es am Ende wird.

Die dümmste Methode die mir einfällt wäre per Reflection eine neue Methode in C zu erstellen die eine Kopie der virtuellen Methode in A ist (einfach Bytecodes kopieren) und die dann aufzurufen.

Reflection ist 'ne schöne Sache. Gibt beinahe nichts, was man mittels Reflection nicht könnte - bloßt eben nur unter FullTrust. Bei MediumTrust sieht es schon wieder anders aus...

Expandable
2006-05-31, 18:46:33
Ich habe mich jetzt mal kurz mit dem Problem beschäftigt und erschreckenderweise festgestellt, dass ich da was komplett falsch verstanden haben muss :( Oder es ist in C# anders als in C++. Hier der Test-Code:


class A
{
public int t = 32;
virtual public void DoSth()
{
Console.WriteLine("A.DoSth()" + t);
}
};

class B : A
{
override public void DoSth()
{
Console.WriteLine("B.DoSth()");
}
};

class C : B
{
new public void DoSth()
{
t = 55;
A a = this;
a.DoSth();
Console.WriteLine("C.DoSth()");
}
};

class Program
{
static void Main(string[] args)
{
C c = new C();
c.DoSth();
return;
}
}


Ich hätte jetzt erwartet, dass a.DoSth() C.DoSth() aufruft - denn DoSth ist doch virtuell, es müsste also in der vTable nachgeschaut werden, festgestellt werden, obwohl es zu A gecastet wurde, ist das Objekt ein C, und dann C.DoSth() aufgerufen werden. Oder? Zumindest würde das in C++ so passieren (nicht getestet in diesem Fall, aber sollte wohl so sein...)

Andere Frage: Warum meckert der Compiler, wenn in C oder B DoSth wieder virtual ist? Warum soll ich da override oder new nehmen? Was wäre in diesem speziellen Fall denn überhaupt der Unterschied zwischen new und override? Wie macht man C++-ähnliche virtuelle Funktionen, die beliebig tief in die Klassen-Hierarchie hineinlangen, so dass garantierterweise immer die richtige Funktion aufgerufen wird?

Kabelsalat
2006-05-31, 19:22:28
Eine als virtual deklarierte Funktion bleibt solange virtuell, bis die Hirarchie mittels dem sealed-Schüsselwort unterbrochen wird, daher meckert der Compiler, wenn du zusätzlich zu dem override-Schlüsselwort noch ein virtual dazu packst. In deinem Fall verkomplizierst du das ganze in dem du ein new in Klasse C mit aufnimmst. C.DoSth() wird daher nur aufgerufen, wenn du es auch es auch "als C verpackst". Anders sieht es bei b aus: A a = new b(); a.DoSth() ruft B.DoSth() aus, da die Funktion virtuell ist.

Gast
2006-05-31, 21:22:20
Interfaces?

Kabelsalat
2006-05-31, 22:59:56
(???)