Einschränkungen bei Generics (Parametrisierte Typen)
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.
Inhaltsverzeichnis
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.