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