Generische Methoden überdecken.



  • Ich habe folgenden C# code:

    public class DoubleEndedQueue<T> : LinkedList<T>
        {
            public LinkedListNode<T> AddFirst<T>(T value)
            {
                return base.AddFirst(value); // <-- geht nicht ?!
            }
       }
    

    Doch beim Versuch die Basis-Methode aufzurufen, meckert der Compiler das der Parameter nicht korrekt sei, die Fehlermeldung:
    Argument '1': cannot convert from 'T' to System.Collections.Generic.LinkedListNode<T>'

    Dabei existieren nur zwei Überladungen der Basisklasse:
    1. public LinkedListNode<T> AddFirst(T value);
    2. public void AddFirst(LinkedListNode<T> node);

    Erstaunlicherweise versucht der Compiler die 2. Version zu verwenden, aber auch ein explizites Casten von value auf (T) value also so:

    return base.AddFirst((T)value); // <-- geht auch nicht ?!
    

    bringt leider nicht den gewünschten Erfolg, die Fehlermeldung bleibt die Gleiche.

    Was habe ich da wichtiges übersehen? 😕



  • Der markierte Teil führt einen neuen Typparameter ein und verursacht so den Fehler.

    BlueForge schrieb:

    public class DoubleEndedQueue<T> : LinkedList<T>
        {
            public LinkedListNode<T> AddFirst[b]<T>[/b](T value)
            {
                return base.AddFirst(value);
            }
       }
    

    Also weg damit.

    Generics sind eben keine Templates. Dein unbeabsichtigt neu eingeführter Typ T hat mit dem Typparameter T der Klasse nichts zu tun. Jede Operation damit müsste also auch mit einem "object" funktionieren. Das trifft aber auf AddFirst nicht zu, da diese Methode ein T (des Klassen-Generics) erwartet.



  • Danke ich hab' gerade selbst festgestellt, bin halt doch noch stark c++ template geschädigt... trotzdem danke 👍 für die ausführliche Erklärung.



  • BlueForge schrieb:

    Danke ich hab' gerade selbst festgestellt, bin halt doch noch stark c++ template geschädigt...

    In einem C++ Klassen-Template hättest du an der Stelle doch auch kein <T> schreiben dürfen 😕



  • @μ Ich fasse das nochmal zusammen, da ich beim ersten Durchlesen nicht so ganz folgen konnte:

    Die "gängige" Klassendefinition, hier ist das T vom generischen Typ T der Klassendefinition:

    class A<T> 
    {
       List<T> foo(T param) 
       { 
          ... // some sense code
       } 
    }
    

    Meine Variante, hier hat das T von foo<T> mit dem Typ T der Klassendefinition B<T> nichts mehr gemeinsam:

    class B<T>
    {
      List<T> foo<T>(T param) // hier ist T was immer param für ein Typ darstellt, 
                              // quasi ein dynamischer Typ. Darüber hinaus über
                              // deckt das lokale T von foo<T> das Klassen T von
                              // B<T>
      {
         ... // some sense code
      }
    }
    

    Und in der Verwendung würde das dann so aussehen:

    // für A
    A<int> a = new A<int>();
    int i = 0;
    a.foo(i); // ok int
    double d = 0.0;
    a.foo(d); // nicht ok, da kein int;
    
    // für B
    B<int> b = new B<int>();
    int i = 0;
    b.foo(i); // ok, siehe nächstes Beispiel
    b.foo("hallo"); // auch ok, denn das [b]int[/b] in B<[b]int[/b]> gilt hier nicht mehr 
                    // (eigentlich auch schon beim b.foo(i) nicht mehr),
                    // sondern nur das was als Typ in den Klammern steht 
                    // (hier und jetzt ist es Typ string), da hier 
                    // das Klassen T von B<[b]T[/b]> vom lokalen T in foo<[b]T[/b]>(T param) 
                    // überschrieben wird. Darüber hinaus, ungeachtet vom T Problem,
                    // ist diese Methoden-Signatur eine Art Freifahrtschein für
                    // die Typisierung, man hätte auch gleich Object satt T
                    // verwenden können, wenn man die Contraints mal nicht beachtet.
    b.foo(0F); // ok, siehe oben
    Object o = new Object();
    b.foo(o); // auch ok, und damit alle Objekte, siehe oben
    

    Ich hoffe das ich nichts wesentliches ausgelassen habe.



  • @hustbaer stimmt schon, aber ich hatte da sowas verrücktes im Kopf:

    // deklaration
    template<typename T>
    class A<T>
    {
      T &foo(T param);
    };
    
    // definition
    T &A<[b]T[/b]>::foo(T param) {...}
    

    Und da ist mir das T irgendwie beim C# Code mit reingerutscht, weil's halt doch noch in Signatur der Definition gehört. Ist natürlich falsch ... :p


Anmelden zum Antworten