Funktionsüberdeckung bei Vererbung



  • Hi,

    in dem eBook, das ich gerade am lesen bin, wird die Funktionsüberdeckung bei Vererbung so erklärt, wie ich sie von C++ aus kenne:

    using System;
    public class Base
    {
        public void Process(int value)
        {
            Console.WriteLine("Base.Process: {0}", value);
        }
    }
    public class Derived: Base
    {
        public void Process(string value)
        {
            Console.WriteLine("Derived.Process: {0}", value);
        }
    }
    class Test
    {
        public static void Main()
        {
            Derived d = new Derived();
            d.Process("Hello");
            d.Process(12);        // Fehler
            ((Base) d).Process(12);    // in Ordnung
        }
    }
    

    Nun habe ich aber festgestellt, dass die mit Fehler gekennzeichnete Zeile mit VS 8 Beta 1 überhaupt keinen Fehler erzeugt. Ist das ein Compiler- oder ein Buchfehler? Oder hat sich da was mit C# 2.0 geändert?



  • Es gibt in keiner Version einen Fehler, da die Methode gar nicht verdeckt, sondern nur überladen wird.



  • Überlagern würde überladen bedeuten, und dafür wäre das Schlüsselwort override nötig. Verdecken heißt ja, dass eine abgeleitet klasse einen gleichen Funktionsnamen hat wie die Basisklasse. Eigentlich der gleiche Vorgang wie das Überladen, man arbeitet nur ohne die Schlüsselwörter Virtual und Override. Der Compiler müsste aber in dem Fall eine Warnung schmeißen, "man solle doch bitte in der Abgeleiteten Klasse den modifizierer NEW verwenden. Dazu müssen aber beide Funktionen die gleiche Argumentliste verwenden. Dein Unterschied zwischen String und INT macht deinen Angeblichen fehler aus.

    Ändere mal in deiner Basisklasse aus der Argumentlist das INT zu einem STRING. Dann meckert dir der Compiler, dass du mit dieser Aktion einen Basisfunktion verdecken würdest, und du bitte mit dem modifizierer new arbeiten sollst, zusätzlich zu ein paar anderen Fehlern :), wie zum Beispiel dass er implizit nicht dazu in der Lage ist einen INT zu einem String zu konvertieren ?

    Also wie gesagt, du verdeckst oder überladest hier nichts, eigentlich verwendest du zwei unterschiedliche Funktionen :).



  • Also versteht man in C# was anderes unter Überdecken als in C++?



  • Im Zusammenhang mit Klassen jedenfalls nicht. Ich weiß jetzt nicht 100%ig aus dem Kopf, wie es genau in C++ mit klassenlosen Funktionen in geschachtelten Namespaces ist. In C# gibt es aber keine globalen Funktionen, also verstehe ich nicht, wo du hier den Unterschied siehst. 😕

    Was du gemacht hast, ist eine Funktion zu überladen und nicht sie zu verdecken. Verdecken geht natürlich nur bei gleicher Signatur, amsonsten sind es doch zwei völlig unterschiedliche Methoden.



  • Sorry war grad noch ein kleiner Denkfehler drin. Du fügst ja letztendlich nur eine zusätzliche Überladung in der abgeleiteten Klasse hinzu.



  • Optimizer schrieb:

    Im Zusammenhang mit Klassen jedenfalls nicht. Ich weiß jetzt nicht 100%ig aus dem Kopf, wie es genau in C++ mit klassenlosen Funktionen in geschachtelten Namespaces ist. In C# gibt es aber keine globalen Funktionen, also verstehe ich nicht, wo du hier den Unterschied siehst. 😕

    Naja, wenn man das Beispiel nach C++ portiert, erzeugt es nen Fehler:

    #include <iostream>
    #include <string>
    
    class Base
    {
    public:
    	void Process(int value)
        {
    		std::cout << "Base.Process: " << value;
        }
    };
    
    class Derived : public Base
    {
    public:
    	void Process(std::string value)
        {
            cout << "Derived.Process: " << value;
        }
    };
    
    int main()
    {
        Derived d = new Derived();
        d.Process("Hello");
        d.Process(12);        // Fehler
        ((Base) d).Process(12);    // in Ordnung
    }
    

    In der C#-Version gibt es dagegen keinen Fehler. *Da* seh ich den Unterschied.

    Was du gemacht hast, ist eine Funktion zu überladen und nicht sie zu verdecken. Verdecken geht natürlich nur bei gleicher Signatur, amsonsten sind es doch zwei völlig unterschiedliche Methoden.

    Ich bin kein Begriffexperte. Nur hätte ich diesen Fall als Verdeckung beschrieben.



  • Stimmt, liefert in C++ nen Fehler. Find ich ja fast schon komisch. Dann sag ich dir jetzt einfach mal, wie es in C# ist.

    Überladen - der Begriff ist sicher klar. Mehrere Methoden haben den selben Namen, aber unterschiedliche Parameterlisten. Die Methoden haben sonst nichts gemeinsam, können jede einzeln virtuell oder nicht-virtuell sein, redefiniert oder verborgen werden.

    Um in C# eine Methode zu redefinieren muss sie virtual oder abstract sein (abstract impliziert virtual) und man muss das override-Schlüsselwort verwenden.

    class Base
    {
        public virtual void foo()    {...}
    }
    class Derived : Base
    {
        public override void foo()    {...}
    }
    

    Nun zu dem Thema, das dich eigentlich interessiert: Verbergen geschieht mit Hilfe des new-Schlüsselworts. Man muss in C# also explizit angeben, ob man eine Methode verbirgt oder redefiniert, wenn es eine Methode mit der selben Parameterliste in der Basisklasse schon gibt.

    class Base
    {
        public void foo()    {...}
    }
    class Derived : Base
    {
        public new void foo()    {...}
    }
    

    Verborgene Methoden können nicht direkt aufgerufen werden. Wenn ich eine Derived-Referenz habe oder mich innerhalb einer Derived-Methode befinde, wird immer Derived.foo() aufgerufen. Ich kann aber durch casten explizit die Methode der Basisklasse aufrufen:

    Derived x = new Derived();
    (x as Base).foo();        // ruft Base.foo() auf
    

    Der Hauptknackpunkt ist eigentlich, dass verborgene Methoden nicht dynamisch gebunden werden. Daher benutzt man das eigentlich nicht sooo oft.

    Überladene Methoden (also mit unterschiedlicher Parameterliste) werden davon überhaupt nicht betroffen. Wenn ich jetzt noch ein foo(int) habe, ist es völlig egal, ob ich foo() redefiniere, verberge oder sonstwas mache, foo(int) verhält sich genauso, als wäre foo() nicht da. Es gibt keinen Zusammenhang zwischen überladenen Methoden.
    Ich finde das Verhalten so logisch und wundere mich gerade, was das in C++ jetzt darstellen soll. Hmmm ne, egal. 🕶



  • Danke für die Erklärung. Hab grad leider keine Zeit, sie ganz zu lesen. Aber zu deiner Entwirrung noch folgende FAQ von HumeSikkins: http://fara.cs.uni-potsdam.de/~kaufmann/?page=GenCppFaqs&faq=Overload#Answ



  • So, habs kapiert. Danke euch allen 🙂


Anmelden zum Antworten