Code

Typableitung in generischen Methoden

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

No Comments

Post a Comment

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.

Wir benutzen Cookies um die Nutzerfreundlichkeit der Webseite zu verbessen. Durch Deinen Besuch stimmst Du dem zu.