Einschränkungen bei Generics (Parametrisierte Typen)

Aus Appmethod Topics
Wechseln zu: Navigation, Suche

Nach oben zu Generics - Index

Einschränkungen können Typparametern von Generics (parametrisierte Typen) zugeordnet werden. Einschränkungen deklarieren Elemente, die jeder konkrete Typ unterstützen muss, bevor er zur Erstellung des generischen Typs diesem Parameter übergeben werden kann.

Festlegen parametrisierter Typen mit Einschränkungen

Einschränkungselemente sind:

  • Null, einer oder mehrere Interface-Typen
  • Null oder ein Klassentyp
  • Die reservierten Wörter "constructor", "class" und "record"

Sie können sowohl "constructor" als auch "class" für eine Einschränkung angeben. Jedoch darf "record" nicht mit anderen reservierten Wörtern kombiniert werden. Mehrere Einschränkungen werden durch logisches UND miteinander verknüpft.

Die folgenden Beispiele zeigen nur Klassentypen, obwohl Einschränkungen für alle Formen parametrisierten Typen gelten.

Deklarieren von Einschränkungen

Einschränkungen werden auf ähnliche Weise wie normale Parameterlisten deklariert:

 type
   TFoo<T: ISerializable> = class
     FField: T;
   end;

In dieser Beispieldeklaration gibt der Typparameter 'T' an, dass das Interface ISerializable unterstützt werden muss. In einer Typkonstruktion wie TFoo<TMyClass> überprüft der Compiler, ob TMyClass tatsächlich ISerializable implementiert.

Mehrere Typparameter

Wenn Sie Einschränkungen mit mehreren Typparametern angeben, müssen Sie diese Parameter wie bei der Deklaration einer Parameterliste durch Semikolons voneinander trennen:

 type
    TFoo<T: ISerializable; V: IComparable>

Und wie bei Parameterdeklarationen können Sie mehrere Typparameter in einer durch Kommas getrennten Liste zusammen gruppieren, um sie an dieselbe Einschränkung zu binden:

 type
    TFoo<S, U: ISerializable> ...

Im obigen Beispiel sind S und U an die ISerializable-Einschränkung gebunden.

Mehrere Einschränkungen

Mehrere Einschränkungen können auf einen einzelnen Typparameter als Kommaliste nach dem Doppelpunkt angewendet werden:

 type
    TFoo<T: ISerializable, ICloneable; V: IComparable> ...

Eingeschränkte Typparameter können mit "freien" Typparametern zusammen verwendet werden. Die folgenden Beispiele sind alle gültig:

 type
    TFoo<T; C: IComparable> ...
    TBar<T, V> ...
    TTest<S: ISerializable; V> ...
    // T und V sind frei, aber C und S sind eingeschränkt

Einschränkungstypen

Einschränkungen für Interface-Typen

Eine Typparametereinschränkung kann keinen, einen oder eine durch Komma getrennte Liste von mehreren Interface-Typen enthalten.

Ein durch einen Interface-Typ eingeschränkter Typparameter bedeutet, dass der Compiler überprüft, ob ein konkreter als Argument an eine Typkonstruktion übergebener Typ die angegebenen Interface-Typen implementiert.

Zum Beispiel:

 type
   TFoo<T: ICloneable> ...
 
   TTest1 = class(TObject, ICloneable)
      ...
   end;
 
   TError = class
   end;
 
 var
   X: TFoo<TTest1>;  //  TTest1 wird hier auf ICloneable-Unterstützung                     
                     //  zur Compilierzeit überprüft
   Y: TFoo<TError>;  //  erwartet: Syntaxfehler hier - TError unterstützt
                     //  ICloneable nicht

Einschränkungen für Klassentypen

Ein Typparameter kann durch keinen oder einen Klassentyp eingeschränkt sein. Wie bei Interface-Typ-Einschränkungen bedeutet diese Deklaration Folgendes: Für den Compiler ist erforderlich, dass alle konkreten, als Argument an den eingeschränkten Typparameter übergebene Typen zuweisungskompatibel mit der Einschränkungsklasse sein müssen.

Die Kompatibilität der Klassentypen folgt den normalen Regeln der OOP-Typkompatibilität - abgeleitete Typen können übergeben werden, wenn Vorfahrtypen erforderlich sind.

Constructor-Einschränkungen

Ein Typparameter kann durch keine oder eine Instanz des reservierten Wortes "constructor" eingeschränkt sein. Das bedeutet, dass der tatsächliche Argumenttyp eine Klasse sein muss, die einen Standardkonstruktor (einen public parameterlosen Konstruktor) definiert, so dass Methoden in dem generischen Typ Instanzen des Argumenttyps mittels des Standardkonstruktors des Argumenttyps konstruieren können, ohne etwas über den Argumenttyp selbst zu wissen (keine minimalen Basistypanforderungen).

In einer Einschränkungsdeklaration können Sie “constructor” in jeder beliebigen Reihenfolge mit Interface- oder Klassentypeinschränkungen zusammen verwenden.

Class-Einschränkung

Ein Typparameter kann durch keine oder eine Instanz des reservierten Wortes "class" eingeschränkt sein. Das bedeutet, dass der tatsächliche Typ ein Klassentyp sein muss.

Record-Einschränkung

Ein Typparameter kann durch keine oder eine Instanz des reservierten Wortes "record" eingeschränkt sein. Das bedeutet, dass der tatsächliche Typ ein Wertetyp sein muss (kein Referenztyp). Eine “record”-Einschränkung kann nicht mit einer “class”- oder “constructor”-Einschränkung kombiniert werden.

Typableitung

Bei der Verwendung eines Feldes oder einer Variablen eines eingeschränkten Typparameters ist in vielen Fällen keine Typumwandlung erforderlich, um das Feld oder die Variable als zu den eingeschränkten Typen gehörig zu behandeln. Der Compiler kann den Typ, auf den Sie verweisen, erschließen, indem anhand des Methodennamens eine Variation von Überladungslösungen für die Methoden ausgeführt wird, die in allen Einschränkungen dieses Typs denselben Namen haben.

Zum Beispiel:

 type
   TFoo<T: ISerializable, ICloneable> = class
     FData: T;
     procedure Test;
   end;
 
 procedure TFoo<T>.Test;
 begin
   FData.Clone;
 end;

Der Compiler sucht "Clone"-Methoden in ISerializable und IClonable, weil FData den Typ T hat, womit gewährleistet ist, dass beide Interfaces unterstützt werden. Wenn beide Interfaces "Clone" mit derselben Parameterliste implementieren, gibt der Compiler einen Fehler bezüglich mehrdeutigem Methodenaufruf aus. Sie müssen dann eine Typumwandlung in eine der beiden Interfaces vornehmen, um die Mehrdeutigkeit aufzulösen.

Siehe auch