Delphi泛型

来源:互联网 发布:win10清理软件 编辑:程序博客网 时间:2024/06/11 20:02

Delphi(pascal的扩展)一直有强大的类型系统。看看下面的实现简单的插入排序算法的示例代码(我使用Delphi 2009年):


01 unit InsSort;
02  
03 interface
04  
05 type
06   TItem = Integer;
07   TArray = array of TItem;
08  
09 procedure InsertionSort(var A: TArray);
10  
11 implementation
12  
13 procedure InsertionSort(var A: TArray);
14 var
15   I, J: Integer;
16   Item: TItem;
17  
18 begin
19   for I:= 1 + Low(A) to High(A) do begin
20     Item:= A[I];
21     J:= I - 1;
22     while (J >= Low(A)) and (A[J] > Item) do begin
23       A[J + 1]:= A[J];
24       Dec(J);
25     end;
26     A[J + 1]:= Item;
27   end;
28 end;


上面的代码对整数数组进行排序,如果我们需要处理字符串数组,我们所要做的就是把TItem声明从整数型转换为字

符串:


1 type
2   TItem = string;


如果我们的任务是进行整数和字符串数组排序,我们应该有两个单独的源代码单元(虽然他们只有一个词是不同的)。显然,这是重复的代码,不能用纯pascal解决。但是,Delphi有泛型,让我们用泛型来试试。

1 procedure InsertionSort<TItem>(var A: array of TItem);


先不要编译,如果我们需要使用泛型类,我们还需要一些手段比较泛型类型,所以我写了下面的简单泛型类:


01 type
02   TArray<TItem> = class
03     class function Compare(const L, R: TItem): Integer; static;
04     class procedure InsertionSort(var A: array of TItem); static;
05   end;
06  
07 implementation
08  
09 uses SysUtils, TypInfo;
10  
11 class function TArray<TItem>.Compare(const L, R: TItem): Integer;
12 var
13   P: PTypeInfo;
14  
15 begin
16   P:= TypeInfo(TItem);
17   case P^.Kind of
18     tkInteger: begin
19       if PInteger(@L)^ < PInteger(@R)^ then Result:= -1
20       else if PInteger(@L)^ > PInteger(@R)^ then Result:= 1
21       else Result:= 0;
22     end;
23     tkUString: Result:= CompareStr(PString(@L)^, PString(@R)^);
24   end;
25 end;
26  
27 class procedure TArray<TItem>.InsertionSort(var A: array of TItem);
28 var
29   I, J: Integer;
30   Item: TItem;
31  
32 begin
33   for I:= 1 + Low(A) to High(A) do begin
34     Item:= A[I];
35     J:= I - 1;
36 //    while (J >= Low(A)) and (A[J] > Item) do begin
37     while (J >= Low(A)) and (Compare(A[J], Item) > 0) do begin
38       A[J + 1]:= A[J];
39       Dec(J);
40     end;
41     A[J + 1]:= Item;
42   end;
43 end;

 

它能工作,不过它看起来很丑陋,如果考虑到泛型比较疯狂的开销的话,特别是像通常由普通汇编指令比较整数类型只用一个单一指令。 Delphi2009的RTL(Generics.Defaults.pas 单元文件)在IComparer泛型接口的基础上提供了更优雅(也更长)的解决方案的,但它归结到底还是使用一个低效率的运行时类型信息。

但是,为什么我们需要'比较'的功能呢?当编译器为泛型TItem类型生成代码时,编译器应该已经知道TItem的实际代替类型,编译器可以利用实际类型来生成更优雅的代码行

1 while .. (A[J] > Item) ..

效率远远高于下面这个:

1 while .. (Compare(A[J], Item) > 0) ..

泛型直接比较的思想还涉及到Haskell的'类型的类'的概念。在'类型类'跟OOP类有很大的不同。 在Haskell中每个类型是许多类型类的实例。例如整数类型是'Eq'类型类的实例,因为整数可以比较相等,也是'ord'类型类的实例,因为所有的比较操作(<,<=,>,> =)适用于整数,也是'Show'类型类,因为整数可以转换为字符串,等等.在我们的情况下,泛型TItem类型应该是一个'ord'类型类的实例。

网友在我以前的贴子评论中指出,我的泛型排序简单例程的TArrayRTL代码包含额外的开销,(Generic.Collections & Generic.Defaults 单元文件),因为是循环内使用RTTI。是的,这是真的。让我们改进RTTI的检查,采取循环外代码,同时保持代码简单(不使用复杂的IComparer接口)。

简单的任务,应该有简单的解决方案。现在,我的解决办法似乎很简单,我也花了几个小时才找到它 -泛型对初学者来说仍然很怪异。最后一步,让系统工作的代码是 在泛型类TArray里面  声明一个泛型过程类型TCompare ,没有它的代码将不能编译:

01unit GenericSort;
02 
03interface
04 
05type
06  TArray<T> = class
07  public type
08    TCompare = function(const L, R: T): Integer;
09  private
10    class procedure InternalSort(var A: array of T;
11      Compare: TCompare); static;
12  public
13    class procedure InsertionSort(var A: array of T); static;
14  end;
15 
16function CompareInt(const L, R: Integer): Integer;
17 
18implementation
19 
20uses SysUtils, TypInfo;
21 
22class procedure TArray.InternalSort(var A: array of T; Compare: TCompare);
23var
24  I, J: Integer;
25  Item: T;
26  P: PTypeInfo;
27 
28begin
29  for I:= 1 + Low(A) to High(A) do begin
30    Item:= A[I];
31    J:= I - 1;
32    while (J >= Low(A)) and (Compare(A[J], Item) > 0do begin
33      A[J + 1]:= A[J];
34      Dec(J);
35    end;
36    A[J + 1]:= Item;
37  end;
38end;
39 
40function CompareInt(const L, R: Integer): Integer;
41begin
42 if L < R then Result:= -1
43  else if L > R then Result:= 1
44  else Result:= 0;
45end;
46 
47class procedure TArray.InsertionSort(var A: array of T);
48var
49  P: PTypeInfo;
50 
51begin
52  P:= TypeInfo(T);
53  case P^.Kind of
54    tkInteger: InternalSort(A, @CompareInt);
55    tkUString: InternalSort(A, @CompareStr);
56  end;
57end;
58 
59end.

请注意该'CompareInt'函数在接口部分声明。如果你注释掉接口声明编译会出错误