Durch ein interessantes Code-Beispiel, hinter dem von Kim Madsen zunächst ein möglicher Bug im Delphi Compiler vermutet wurde, habe ich mir das Aufruf-Verhalten von überladenen Methoden, die auch generische Typ Parameter enthalten, genauer angesehen. Ich war von dem möglichen Fehler nämlich überrascht, da ich ziemlich sicher in aktuellen Projekten Overloads mit Kombinationen aus generischen und nicht-generischen Methoden verwende, ohne das offensichtliche Fehler auftreten.
Hier eine vereinfachte Darstellung von Kim’s Code:
TFoo = class(TObject)
public
function DoSomething(SomeObject: TValue): string; overload;
function DoSomething<T>(SomeObject: T): string; overload;
end;
TBar = class(TObject)
end;
//...
var Foo := TFoo.Create;
var Bar := TBar.Create;
Foo.DoSomething(Bar);
Welche der beiden möglichen Varianten von DoSomething sollte durch Foo.DoSomething(Bar) aufgerufen werden?
Im ersten Ansatz war ich geneigt zu sagen, dass die nicht-generische Variante, also function DoSomething(SomeObject: TValue): string; aufgrufen werden sollte, da ja schliesslich kein Typ-Parameter angegeben ist und daher die erste Variante die am ehesten passende ist.
Tatsächlich wird aber die generische, zweite Variante aufgerufen. Ich hatte zunächst „TValue“ im Verdacht, hier die Overload-Auflösung zu „blockieren“, da TValue ja quasi ein „künstlicher“ RTTI-.getriebener Typ ist.
Stefan Glienke hatte aber letztlich den passenden Hinweis:
Generische Parameter sind tatsächlich optional und der Typ wird durch den Typ der Variablen, die übergeben wird, automatisch abgeleitet. Diese „Type-Inferrence“ hat Vorrang vor dem Auflösen von Overloads und erzeugt damit einen besseren Match als die Methode mit „TValue“.
Die Typableitung bei generischen Methoden ist übrigens hier dokumentiert.
Der folgende Code ruft also tatsächlich eine generische Implementation auf, obwohl kein Typ-Parameter im Aufruf angegeben ist:
function DoSomething<T>(SomeObject: T): string;
//...
Foo.DoSomething(Bar); //<T> is inferred from "Bar"
Hier ein Gist mit komplettem Code zum Ausprobieren:
https://gist.github.com/omonien/24b9b4ba64d46bdfa478615e8e9b0ae6