|
RAD Studio (Common)
|
The declaration of a generic is similar to the declaration of a regular class, record, or interface type. The difference is that a list of one or more type parameters placed between angle brackets (< and >) follows the type identifier in the declaration of a generic.
Type parameters can be used as a typical type identifier inside the container type declaration and method body.
For example:
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;Generic types are instantiated by providing type arguments. In Delphi, you can use any type as a type argument except for the following: a static array, a short string, or a record type that (recursively) contains a field of one or more of these two types.
type
TFoo<T> = class
FData: T;
end;
var
F: TFoo<Integer>; // 'Integer' is the type argument of TFoo<T>
begin
,,,
end.A nested type within a generic is itself a generic.
type
TFoo<T> = class
type
TBar = class
X: Integer;
// ...
end;
// ... TBaz = class
type
TQux<T> = class
X: Integer;
// ...
end;
// ...
end;To access the TBar nested type, you must specify a construction of the TFoo type first:
var
N: TFoo<Double>.TBar;A generic can also be declared within a regular class as a nested type:
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;The base type of a parameterized class or interface type might be an actual type or a constructed type. The base type might not be a type parameter alone.
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;If TFoo2<String> is instantiated, an ancestor class becomes TBar2<String>, and TBar2<String> is automatically instantiated.
Class, interface, and record types can be declared with type parameters.
For example:
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;The procedure type and the method pointer can be declared with type parameters. Parameter types and result types can also use type parameters.
For example:
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.
var
F: TFoo;
begin
F := TFoo.Create;
F.Test;
F.Free;
end.Methods can be declared with type parameters. Parameter types and result types can use type parameters Parameterized methods are similar to overloaded methods.
There are two ways to instantiate a method:
For example:
type
TMyProc2<Y> = procedure(Param1, Param2: Y) of object;
TFoo = class
procedure Test;
procedure MyProc2<T>(X, Y: T);
end;
procedure TFoo.MyProc2<T>(X, Y: T);
begin
Write('MyProc2<T>');
{$IFDEF CIL}
Write(X.ToString);
Write(', ');
WriteLn(Y.ToString);
{$ENDIF}
WR
end;
procedure TFoo.Test;
var
P: TMyProc2<Integer>;
begin
MyProc2<String>('Hello', 'World'); //type specified
MyProc2('Hello', 'World'); //inferred from argument type
MyProc2<Integer>(10, 20);
MyProc2(10, 20);
P := MyProc2<Integer>;
P(40, 50);
end;
var
F: TFoo;
begin
F := TFoo.Create;
F.Test;
F.Free;
end.The scope of a type parameter covers the type declaration and the bodies of all its members, but does not include descendant types.
For example:
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.|
Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
|
|
What do you think about this topic? Send feedback!
|