Deklaration von Generics
Nach oben zu Generics - Index
Die Deklaration von Generics ist vergleichbar mit der Deklaration von regulären Klassen-, Record- oder Interface-Typen. Der Unterschied besteht darin, dass die Deklaration von Generics eine Liste mit einem oder mehreren Typparametern in spitzen Klammern (< und >) nach dem Typbezeichner enthält.
Ein Typparameter kann als typischer Typbezeichner innerhalb der Container-Typdeklaration und im Methodenrumpf verwendet werden.
Zum Beispiel:
type
TPair<TKey,TValue> = class // TKey and TValue are type parameters
FKey: TKey;
FValue: TValue;
function GetValue: TValue;
end;
function TPair<TKey,TValue>.GetValue: TValue;
begin
Result := FValue;
end;
Inhaltsverzeichnis
Typargument
Generische Typen werden durch das Bereitstellen von Typargumenten instantiiert. In Delphi können Sie jeden beliebigen Typ außer den folgenden als Typargument verwenden: ein statisches Array, ein Short String oder ein Record-Typ, der ein Feld mit einem oder mehreren dieser beiden Typen (rekursiv) enthält.
type
TFoo<T> = class
FData: T;
end;
var
F: TFoo<Integer>; // 'Integer' is the type argument of TFoo<T>
begin
...
end.
Verschachtelte Typen
Ein in einem generischen Typ verschachtelter Typ ist selbst ein generischer Typ.
type
TFoo<T> = class // A generic class
type
TBar = class
X: Integer;
end;
end;
Um auf den verschachtelten Typ TBar zuzugreifen, müssen Sie zuerst eine Konstruktion des Typs TFoo angeben:
var
N: TFoo<Double>.TBar;
Ein generischer Typ kann auch in einer regulären Klasse als verschachtelter Typ deklariert werden:
type
TBaz = class // A regular class
type
TQux<T> = class
X: Integer;
end;
end;
type
TOuter = class
type
TData<T> = class
FFoo1: TFoo<Integer>; // declared with closed constructed type
FFoo2: TFoo<T>; // declared with open constructed type
FFooBar1: TFoo<Integer>.TBar; // declared with closed constructed type
FFooBar2: TFoo<T>.TBar; // declared with open constructed type
FBazQux1: TBaz.TQux<Integer>; // declared with closed constructed type
FBazQux2: TBaz.TQux<T>; // declared with open constructed type
...
end;
var
FIntegerData: TData<Integer>;
FStringData: TData<String>;
end;
Basistypen
Der Basistyp eines parametrisierten Klassen- oder Interface-Typs kann ein tatsächlicher Typ oder ein konstruierter Typ sein. Der Basistyp darf kein Typparameter sein.
type
TFoo1<T> = class(TBar) // Actual type
end;
TFoo2<T> = class(TBar2<T>) // Open constructed type
end;
TFoo3<T> = class(TBar3<Integer>) // Closed constructed type
end;
Wenn TFoo2<String> instantiiert ist, wird eine Vorfahrklasse zu TBar2<String>, und TBar2<String> wird automatisch instantiiert.
Klassen-, Interface- und Record-Typen
Klassen-, Interface-, Record- und Array-Typen können mit Typparametern deklariert werden.
Zum Beispiel:
type
TRecord<T> = record
FData: T;
end;
type
IAncestor<T> = interface
function GetRecord: TRecord<T>;
end;
IFoo<T> = interface(IAncestor<T>)
procedure AMethod(Param: T);
end;
type
TFoo<T> = class(TObject, IFoo<T>)
FField: TRecord<T>;
procedure AMethod(Param: T);
function GetRecord: TRecord<T>;
end;
type
anArray<T>= array of T;
IntArray= anArray<integer>;
Prozedurale Typen
Prozedurale Typen und Methodenzeiger können mit Typparametern deklariert werden. Parametertypen und Ergebnistypen können auch Typparameter verwenden.
Zum Beispiel:
type
TMyProc<T> = procedure(Param: T);
TMyProc2<Y> = procedure(Param1, Param2: Y) of object;
type
TFoo = class
procedure Test;
procedure MyProc(X, Y: Integer);
end;
procedure Sample(Param: Integer);
begin
Writeln(Param);
end;
procedure TFoo.MyProc(X, Y: Integer);
begin
Writeln('X:', X, ', Y:', Y);
end;
procedure TFoo.Test;
var
X: TMyProc<Integer>;
Y: TMyProc2<Integer>;
begin
X := Sample;
X(10);
Y := MyProc;
Y(20, 30);
end;
var
F: TFoo;
begin
F := TFoo.Create;
F.Test;
F.Free;
end.
Parametrisierte Methoden
Methoden können mit Typparametern deklariert werden. Parametertypen und Ergebnistypen können Typparameter verwenden. Konstruktoren und Destruktoren sowie virtuelle, dynamische oder Botschaftsmethoden dürfen allerdings keine Typparameter haben. Parametrisierte Methoden sind mit überladenen Methoden vergleichbar.
Es gibt zwei Möglichkeiten, eine Methode zu instantiieren:
- Durch die explizite Angabe eines Typarguments
- Durch automatische Ableitung vom Typargument
Zum Beispiel:
type
TFoo = class
procedure Test;
procedure CompareAndPrintResult<T>(X, Y: T);
end;
procedure TFoo.CompareAndPrintResult<T>(X, Y: T);
var
Comparer : IComparer<T>;
begin
Comparer := TComparer<T>.Default;
if Comparer.Compare(X, Y) = 0 then
WriteLn('Both members compare as equal')
else
WriteLn('Members do not compare as equal');
end;
procedure TFoo.Test;
begin
CompareAndPrintResult<String>('Hello', 'World');
CompareAndPrintResult('Hello', 'Hello');
CompareAndPrintResult<Integer>(20, 20);
CompareAndPrintResult(10, 20);
end;
var
F: TFoo;
begin
F := TFoo.Create;
F.Test;
ReadLn;
F.Free;
end.
Gültigkeitsbereich von Typparametern
Der Gültigkeitsbereich eines Typparameters umfasst die Typdeklaration und den Rumpf aller Member, aber keine abgeleiteten Typen.
Zum Beispiel:
type
TFoo<T> = class
X: T;
end;
TBar<S> = class(TFoo<S>)
Y: T; // error! unknown identifier "T"
end;
var
F: TFoo<Integer>;
begin
F.T // error! unknown identifier "T"
end.