Delphi 在编译时复制具有未知基类型的泛型对象

标签 delphi generics

我想复制通用对象,但其类型只能通过运行时的“类”构造来获取,因为源对象类型可能不同(TItem 或 TSpecificItem 等):

type
  TItem = class
  //...
    procedure Assign(Source: TItem);virtual; abstract; //edit
  end;

  TSpecificItem = class(TItem)
  //...
  end;

  TEvenMoreSpecificItem = class(TSpecificItem)
  //...
  end;

  TItemClass = class of TItem;

  TItemContainer = class
    FItems: TObjectList<TItem>; //edit
    procedure Assign(Source: TObject); //edit
    function GetItem(Index: Integer): TItem; inline; //edit
    procedure SetItem(Index: Integer; Item: TItem); inline; //edit
    function Count: Integer; //edit;
    function ItemClass: TItemClass; virtual; abstract;
    property Items[Index: Integer]: TItem read GetItem write SetItem; //edit
  end;

  TItemContainer<T: TItem> = class(TItemContainer)
  //...
    function GetItem(Index: Integer): T; inline; //edit
    procedure SetItem(Index: Integer; Item: T); inline; //edit
    function ItemClass: TItemClass; override;
    property Items[Index: Integer]: T read GetItem write SetItem; default; //edit
  end;

//start of edit
function TItemContainer.Count: Integer;
begin
  Result := FItems.Count;
end;

function TItemContainer.GetItem(Index: Integer): TItem;
begin
  Result := FItems[Index];
end;

procedure TItemContainer.SetItem(Index: Integer; Item: TItem);
begin
  FItems[Index].Assign(Item);
end;

procedure TItemContainer.Assign(Source: TObject);
var
  I: Integer;
  Item: TItem;
  Cls: TClass;
begin
  if Source is TItemContainer then
  begin
    FItems.Clear;
    for I := 0 to TItemContainer(Source).Count - 1 do
    begin
      Item := TItemContainer(Source).Items[I];
      Cls := Item.ClassType;
      Item := TItemClass(Cls).Create;
      Item.Assign(TItemContainer(Source).Items[I]);
      FItems.Add(Item);
    end;
  end;
end;

function TItemContainer<T>.GetItem(Index: Integer): T;
begin
  Result := T(inherited GetItem(Index));
end;

procedure TItemContainer<T>.SetItem(Index: Integer; Item: T);
begin
  inherited SetItem(Index, Item);
end;
//end of edit

function TItemContainer<T>.ItemClass: TItemClass;
begin
  Result := TItemClass(GetTypeData(PTypeInfo(TypeInfo(T)))^.ClassType);
end;

function CopyGenericObject(Source: TItemContainer): TItemContainer;
var
  Cls: TItemClass;
begin
  Cls := Source.ItemClass;
  Result := TItemContainer<Cls>.Create; // compiler reports error "incompatible types"
  Result.Assign(Source);
end;

// edit:
procedure DoCopy;
var
  Source: TItemContainer<TEvenMoreSpecificItem>;
  Dest: TItemContainer;
begin
  Source := TItemContainer<TEvenMoreSpecificItem>.Create; // for example
  //add some items to Source
  Dest := CopyGenericObject(Source);
  //use the result somewhere
end;

我必须使用Delphi XE。

我发现了 http://docwiki.embarcadero.com/RADStudio/XE6/en/Overview_of_Generics

Dynamic instantiation

Dynamic instantiation at run time is not supported.

这是我想做的吗?

最佳答案

如果我理解得很好,您正在寻找的是实现一个例程,该例程将创建与给定源类型相同的类的实例。这可以这样完成:

type
  TItemContainerclass = class of TItemContainer;

function CopyGenericObject(Source: TItemContainer): TItemContainer;
begin
  Result := TItemContainerclass(Source.ClassType).Create; 
end;

此外,您可以将 ItemClass 例程简化为

function TItemContainer<T>.ItemClass: TItemClass;
begin
  Result := T;
end;

请注意,这只会创建一个新的实例,而不是源的副本,但由于您的代码没有显示任何复制对象的尝试,并且仅创建一个新实例,我认为这是您想要的结果。

注意:这适用于 Delphi 10,我无法访问 XE 来测试它。

关于Delphi 在编译时复制具有未知基类型的泛型对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34510995/

相关文章:

delphi - 如何使用 TEdit 框输入密码而不显示输入的字符而仅显示 *

delphi - 捕获 CTRL + S 时如何摆脱 Windows 声音?

Delphi7、dbExpress和Master Detail关系

generics - 通用 F# 函数 : How to get the Type of an F# Discriminated Union?

c# - 确定字段是否使用通用参数

delphi - 如何配置 FastMM 来检测 dll 中的内存泄漏

Java/C/C++/C#/PHP 到 Pascal 转换器?

.net - 为什么 Dictionary<TKey, TValue> 没有 IEnumerable<KeyValuePair<TKey, TValue>> ctor?

java - Spring应用程序找不到映射 View

generics - 具有 ifort 编译器的通用内部过程