泛型

泛型

声明泛型类

泛型类的声明方式:

1
2
3
4
type
类名<类型参数1,类型参数2, ...> = class(父类)
...
end;

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type
TSample<T> = class
private
type
TInnerRec<R> = record
i: R;
end;
TQux<T> = class
X: Integer;
Y: T;
end;
public
FSample: T;
function fun<F>(value: F): F;
end;

类型参数是一个局部化的标识符,所以 TSample<T> 中的 T 只能用于这个类中,TInnerRec<R> 中的 R 也只能用于这个内部记录中。同理 F 参数也只能用于 TSample<T>.fun<F> 这个泛型方法中。

TSample<T> 为例,其中声明了套嵌类 TQux<T>,这个 T 参数只能用于 TQux<T> 中,由于这个参数的名称与 TSample<T> 中的类型参数的名称相同,所以 TSample<T> 中的类型参数在 TQux<T> 被屏蔽。

泛型方法重载

泛型方法重载,一样是在后面加 overload 指示字:

1
2
3
4
5
6
7
8
9
10
11
12
13
type
T1<T> = record
procedure M1<T>(A: T); overload; // 泛型版本
procedure M1(A: String); overload; // 非泛型版本(普通版本)
procedure Test;
end;
//
var
obj: T1<string>;
begin
obj.M1('delphi');
readln;
end.

上述泛型记录的实例化用了 string 类型。 上述的调用 obj.M1('delphi'); 既有泛型版本 M1<string> 可以匹配,也有普通版本的 M1 可以匹配。

在重载方法的泛型版本与非泛型版本间,编译器优先选择非泛型版本。

泛型的限定

接口限定

类型参数后面加冒号 : ,后面带上一个或者多个接口名,这样就限定泛型实例化时,该类型或者其父类必须实现这些接口:

1
2
3
4
type
TCon<T: Interface1> = class // T 必须直接实现 Interface1 接口
//
end;

类名限定

类型参数后面加冒号 : ,后面带上一个类名。表示实例化时,必须赋值兼容。也就是类型参数必须是指定类、nil 或者其派生类。

1
2
3
4
type
TCon<T: Class1> = class // T 必须是 Class1 或者其派生类,或者 nil
//
end;

Delphi 中不允许直接使用 TObject 来进行限定

constructor 限定

限定类型必须带一个无参的构造函数。

1
2
3
4
type
TCon<T: constructor> = class
//
end;

class 限定

限定类型必须是一个类:

1
2
3
4
type
TCon<T: class> = class
//
end;

record 限定

限定类型必须是一个记录:

1
2
3
4
type
TCon<T: record> = class
//
end;

多种限定组合

以上介绍的五种限定可以联合使用,但是注意:

  • classrecord 关键词不能与类名称联合限定。
  • record 关键词也不能与 classconstructor 是的任何一个联用

TList

TList 是一个智能化的动态数组 (需要uses Generics.Collections 单元)

下面是它的一些常见的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
TList<T> = class(TEnumerable<T>)
...
constructor Create; overload;
destructor Destroy; override;
function Add(const Value: T): Integer;
procedure AddRange(const Values: array of T); overload;
procedure Insert(Index: Integer; const Value: T);
procedure InsertRange(Index: Integer; const Values: array of T); overload;
function Remove(const Value: T): Integer;
procedure Delete(Index: Integer);
procedure DeleteRange(AIndex, ACount: Integer);
function Extract(const Value: T): T;
procedure Clear;
procedure Exchange(Index1, Index2: Integer);
procedure Move(CurIndex, NewIndex: Integer);
procedure Sort; overload;
function Contains(const Value: T): Boolean;
function IndexOf(const Value: T): Integer;
function LastIndexOf(const Value: T): Integer;
property Capacity: Integer read GetCapacity write SetCapacity;
property Count: Integer read FCount write SetCount;
property Items[Index: Integer]: T read GetItem write SetItem; default;
...
end;