Delphi 泛型约束问题

Posted

技术标签:

【中文标题】Delphi 泛型约束问题【英文标题】:Delphi Generic constraints problem 【发布时间】:2009-02-24 01:17:13 【问题描述】:

我正在尝试创建一个通用列表类以与 tiOPF(delphi @ www.tiopf.com 的对象持久性框架)一起使用。具体来说,我正在尝试采用现有的通用类(TtiObjectList)并制作一个使用 TtiObject 后代的通用版本。

我更改基类的范围有限,因为它们需要在 D7 - D2009 和 Free Pascal 下编译。我需要从 TtiObjectList 降级以保持现有的持久性机制正常工作。

// base class  
type  
  TtiObjectList = class(TtiObject)
...  
protected  
  function GetItems(i: integer): TtiObject; virtual;  
  procedure SetItems(i: integer; const AValue: TtiObject); virtual;  
...  
public  
  function Add(const AObject : TtiObject): integer; overload; virtual;  
...  
end;  

我的类定义如下:

TtiGenericObjectList<T: TtiObject> = class(TtiObjectList)  
protected  
  function GetItems(i:integer): T; reintroduce;  
  procedure SetItems(i:integer; const Value: T); reintroduce;  
public  
  function Add(const AObject: T): integer; reintroduce;  
  property Items[i:integer]: T read GetItems write SetItems; default;  
end;

implementation

 TtiGenericObjectList<T> 

function TtiGenericObjectList<T>.Add(const AObject: T): integer;  
var obj: TtiObject;  
begin  
  obj:= TtiObject(AObject); /// Invalid typecast
  result:= inherited Add(obj);  
end;  

// alternate add, also fails  
function TtiGenericObjectList<T>.Add(const AObject: T): integer;  
begin  
  result:= inherited Add(AObject); /// **There is no overloaded version**
 /// **of 'Add' that can be called with these arguments**  
end;  

function TtiGenericObjectList<T>.GetItems(i: integer): T;  
begin  
  result:= T(inherited GetItems(i)); /// **Invalid typecast  **
end;  

procedure TtiGenericObjectList<T>.SetItems(i: integer; const Value: T);  
begin  
  inherited SetItems(i, Value);  
end;  

我遇到的问题是 delphi 没有将 T 视为 TtiObject 后代。当我执行以下操作时,我收到了无效的类型转换错误:

function TtiGenericObjectList<T>.Add(const AObject: T): integer;  
var obj: TtiObject;  
begin  
  obj:= TtiObject(AObject); /// **Invalid typecast***
  result:= inherited Add(obj);  
end;  

如果我不进行类型转换,则会收到重载错误,如上面的清单所示。

任何想法我哪里出错了?

肖恩

【问题讨论】:

这是 Delphi 2009 编译器中的一个错误;我认为它已在 Delphi 2010 编译器中解决。 【参考方案1】:

Delphi 2009 编译器在其泛型实现中存在一些非常严重的缺陷。它几乎没有像它应该理解的那样理解约束的含义,(Barry Kelly 在 SO 的其他地方承认了这一点;我不记得确切的位置,)并且跨单元泛型会导致非常奇怪的问题。最好的办法是根据具体情况处理这个问题:如果您的代码可以编译,请使用它。如果没有,请返回到非通用实现,直到他们修复它。希望我们能在不久的将来看到修复泛型(和 Generics.Collections 单元)的更新。

【讨论】:

【参考方案2】:

看来问题不是我,而是编译器:)。

最后,我用以下方法破解了它

class function TtiGenericObjectList<T>.GenericAsObject(const Value): TObject;
begin
  Result := TObject(Value);
end;

class function TtiGenericObjectList<T>.ObjectAsGeneric(const Value): T;
begin
  Result := T(Value);
end;

如下使用

function TtiGenericObjectList<T>.Add(const AObject: T): integer;
var obj: TtiObject;
begin
  obj:= TtiObject(GenericAsObject(AObject));
  result:= inherited Add(obj);
  // replaces the following which gets overload errors
//   result:= inherited Add(TtiObject(AObject));
end;

function TtiGenericObjectList<T>.GetItems(i: integer): T;
var obj: TtiObject;
begin
  obj:= inherited GetItems(i);
  result:= ObjectAsGeneric(obj);
  // replaces the following which gets "Invalid typecast" errors
  // result:= inherited Add(AObject);
end;

我会清理一下这些并使用它们直到编译器得到修复。

【讨论】:

【参考方案3】:

我最近使用 D2010 解决了一个类似的问题,这是我想出的代码。

type
  TGenericList<T: TtiObject> = class(TtiObjectList)
  protected
    function GetItems(AIndex: integer): T; reintroduce;
    procedure SetItems(AIndex: integer; const AValue: T); reintroduce;
  public
    property Items[i:integer]: T read GetItems write SetItems; default;
    function Add(const AObject: T): integer; reintroduce;
  end;

implementation

 TGenericList<T> 

function TGenericList<T>.Add(const AObject: T): integer;
begin
  Result := inherited Add(TtiObject(AObject));
end;

function TGenericList<T>.GetItems(AIndex: integer): T;
begin
  Result := T(inherited GetItems(AIndex));
end;

procedure TGenericList<T>.SetItems(AIndex: integer; const AValue: T);
begin
  inherited SetItems(AIndex, AValue);
end;

【讨论】:

【参考方案4】:

我发现了泛型的一些小问题,对于这样一个新功能来说这并不奇怪。而且您正试图真正混淆编译器;-)。

很遗憾,我手头没有 2009,所以我无法测试,但我有一些建议:

您是否查找过更新(并安装了它们)?

您是否尝试过使用 as 和 is 运算符:

obj:= AObject as TtiObject;

您是否尝试过使用中间类:

TtiGenericList<T: TObject> = class(TtiObjectList)  
protected  
  function GetItems(i:integer): T; reintroduce;  
  procedure SetItems(i:integer; const Value: T); reintroduce;  
public  
  function Add(const AObject: T): integer; reintroduce;  
  property Items[i:integer]: T read GetItems write SetItems; default;  
end;

【讨论】:

【参考方案5】:

(由于类似的问题,我来到这里,但在确定它时我得到了一个不同的解决方案,尽管使用的是更新的(XE)Delphi):

或者干脆尝试将局部变量 Obj 设为 T 类型。

【讨论】:

以上是关于Delphi 泛型约束问题的主要内容,如果未能解决你的问题,请参考以下文章

我如何在不使用Delphi约束的情况下确定泛型类型为nil?

C#泛型方法,泛型约束问题

如何在 Delphi 中测试泛型类型变量是不是与 Default(T) 相等?

delphi2010泛型练习

仅适用于数值类型的泛型类型约束

请教一个unity有关于泛型参数的问题