You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi中如何高效实现从TList<class>到TList<interface>的转换?

高效实现Delphi中TList到接口列表的转换

针对你遇到的TList转接口列表的效率问题,以下是几种实用的解决方案,覆盖不同场景需求:


方案1:只读包装类(零复制,最高效)

核心思路是实现一个包装类,直接映射原TObjectList的内容,返回接口类型的列表视图,无需复制任何元素。外部只能读取数据,无法修改原列表结构,保证数据安全的同时完全避免遍历开销。

包装类实现

unit InterfaceListWrapper;

interface

uses
  Generics.Collections, Generics.Defaults;

type
  TInterfaceListWrapper<T: class; I> = class(TInterfacedObject, IList<I>)
  strict private
    FInnerList: TObjectList<T>;
  public
    constructor Create(InnerList: TObjectList<T>);
    function GetCount: Integer;
    function GetItem(Index: Integer): I;
    procedure SetItem(Index: Integer; const Value: I);
    function Add(const Value: I): Integer;
    procedure Clear;
    function Contains(const Value: I): Boolean;
    function IndexOf(const Value: I): Integer;
    procedure Insert(Index: Integer; const Value: I);
    procedure Remove(const Value: I);
    procedure Delete(Index: Integer);
    function GetEnumerator: TEnumerator<I>;
    property Count: Integer read GetCount;
    property Items[Index: Integer]: I read GetItem write SetItem; default;
  end;

implementation

uses
  SysUtils;

{ TInterfaceListWrapper<T, I> }

constructor TInterfaceListWrapper<T, I>.Create(InnerList: TObjectList<T>);
begin
  inherited Create;
  FInnerList := InnerList;
end;

function TInterfaceListWrapper<T, I>.GetCount: Integer;
begin
  Result := FInnerList.Count;
end;

function TInterfaceListWrapper<T, I>.GetItem(Index: Integer): I;
begin
  Result := FInnerList[Index] as I;
end;

procedure TInterfaceListWrapper<T, I>.SetItem(Index: Integer; const Value: I);
begin
  raise ENotSupportedException.Create('列表为只读');
end;

function TInterfaceListWrapper<T, I>.Add(const Value: I): Integer;
begin
  raise ENotSupportedException.Create('列表为只读');
end;

procedure TInterfaceListWrapper<T, I>.Clear;
begin
  raise ENotSupportedException.Create('列表为只读');
end;

function TInterfaceListWrapper<T, I>.Contains(const Value: I): Boolean;
begin
  if Value is T then
    Result := FInnerList.Contains(Value as T)
  else
    Result := False;
end;

function TInterfaceListWrapper<T, I>.IndexOf(const Value: I): Integer;
begin
  if Value is T then
    Result := FInnerList.IndexOf(Value as T)
  else
    Result := -1;
end;

procedure TInterfaceListWrapper<T, I>.Insert(Index: Integer; const Value: I);
begin
  raise ENotSupportedException.Create('列表为只读');
end;

procedure TInterfaceListWrapper<T, I>.Remove(const Value: I);
begin
  raise ENotSupportedException.Create('列表为只读');
end;

procedure TInterfaceListWrapper<T, I>.Delete(Index: Integer);
begin
  raise ENotSupportedException.Create('列表为只读');
end;

function TInterfaceListWrapper<T, I>.GetEnumerator: TEnumerator<I>;
type
  TInterfaceEnumerator = class(TEnumerator<I>)
  strict private
    FInnerEnum: TEnumerator<T>;
  protected
    function DoGetCurrent: I; override;
    function DoMoveNext: Boolean; override;
  public
    constructor Create(InnerEnum: TEnumerator<T>);
    destructor Destroy; override;
  end;

begin
  Result := TInterfaceEnumerator.Create(FInnerList.GetEnumerator);
end;

{ TInterfaceEnumerator }

constructor TInterfaceListWrapper<T, I>.TInterfaceEnumerator.Create(InnerEnum: TEnumerator<T>);
begin
  inherited Create;
  FInnerEnum := InnerEnum;
end;

destructor TInterfaceListWrapper<T, I>.TInterfaceEnumerator.Destroy;
begin
  FInnerEnum.Free;
  inherited;
end;

function TInterfaceListWrapper<T, I>.TInterfaceEnumerator.DoGetCurrent: I;
begin
  Result := FInnerEnum.Current as I;
end;

function TInterfaceListWrapper<T, I>.TInterfaceEnumerator.DoMoveNext: Boolean;
begin
  Result := FInnerEnum.MoveNext;
end;

end.

修改原类的GetChucks方法

unit Unit11;

interface

uses
  Generics.Collections, InterfaceListWrapper;

type
  IChuck = interface
  end;

  TChuck = class(TInterfacedObject, IChuck)
  end;

  TProgram = class
  strict private
    FChucks: TObjectList<TChuck>;
  private
    function GetChucks: IList<IChuck>;
  public
    constructor Create;
    destructor Destroy; override;
    property Chucks: IList<IChuck> read GetChucks;
  end;

implementation

uses
  SysUtils;

{ TProgram }

constructor TProgram.Create;
begin
  FChucks := TObjectList<TChuck>.Create;
end;

destructor TProgram.Destroy;
begin
  FreeAndNil(FChucks);
  inherited;
end;

function TProgram.GetChucks: IList<IChuck>;
begin
  // 返回包装类实例,无元素复制
  Result := TInterfaceListWrapper<TChuck, IChuck>.Create(FChucks);
end;

end.

方案2:延迟缓存+失效机制(读多写少场景)

如果需要外部可修改接口列表,或者必须返回TList类型,可以采用缓存机制:只在原列表发生变化时重新生成缓存,读操作直接返回缓存结果,减少重复遍历开销。

修改原类实现

unit Unit11;

interface

uses
  Generics.Collections;

type
  IChuck = interface
  end;

  TChuck = class(TInterfacedObject, IChuck)
  end;

  TProgram = class
  strict private
    FChucks: TObjectList<TChuck>;
    FChucksCache: TList<IChuck>;
    FCacheValid: Boolean;
    procedure InvalidateCache;
  private
    function GetChucks: TList<IChuck>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure AddChuck(AChuck: TChuck);
    procedure RemoveChuck(AChuck: TChuck);
    procedure ClearChucks;
    property Chucks: TList<IChuck> read GetChucks;
  end;

implementation

uses
  SysUtils;

{ TProgram }

constructor TProgram.Create;
begin
  FChucks := TObjectList<TChuck>.Create;
  FChucksCache := TList<IChuck>.Create;
  FCacheValid := False;
end;

destructor TProgram.Destroy;
begin
  FChucksCache.Free;
  FChucks.Free;
  inherited;
end;

procedure TProgram.InvalidateCache;
begin
  FCacheValid := False;
end;

procedure TProgram.AddChuck(AChuck: TChuck);
begin
  FChucks.Add(AChuck);
  InvalidateCache;
end;

procedure TProgram.RemoveChuck(AChuck: TChuck);
begin
  FChucks.Remove(AChuck);
  InvalidateCache;
end;

procedure TProgram.ClearChucks;
begin
  FChucks.Clear;
  InvalidateCache;
end;

function TProgram.GetChucks: TList<IChuck>;
var
  LChuck: TChuck;
begin
  if not FCacheValid then
  begin
    FChucksCache.Clear;
    for LChuck in FChucks do
      FChucksCache.Add(LChuck);
    FCacheValid := True;
  end;
  Result := FChucksCache;
end;

end.

方案3:直接存储接口列表(代码最简)

如果业务允许,可以直接将内部存储改为TList,彻底避免类型转换问题。需要注意接口引用计数的生命周期管理:如果原类依赖TObjectList的OwnsObjects特性,需确保对象不会被提前释放或泄漏。

简化实现

unit Unit11;

interface

uses
  Generics.Collections;

type
  IChuck = interface
  end;

  TChuck = class(TInterfacedObject, IChuck)
  end;

  TProgram = class
  strict private
    FChucks: TList<IChuck>;
  private
    function GetChucks: TList<IChuck>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure AddChuck(AChuck: TChuck);
    property Chucks: TList<IChuck> read GetChucks;
  end;

implementation

uses
  SysUtils;

{ TProgram }

constructor TProgram.Create;
begin
  FChucks := TList<IChuck>.Create;
end;

destructor TProgram.Destroy;
begin
  FChucks.Free;
  inherited;
end;

procedure TProgram.AddChuck(AChuck: TChuck);
begin
  FChucks.Add(AChuck); // 自动转换为IChuck接口
end;

function TProgram.GetChucks: TList<IChuck>;
begin
  Result := FChucks;
end;

end.

方案对比

方案优点缺点适用场景
只读包装类零复制、性能最高、数据安全仅支持只读操作外部只需读取数据的场景
延迟缓存+失效机制读操作高效、支持修改需要维护缓存失效逻辑读多写少的业务场景
直接存储接口列表代码最简、无转换开销需处理引用计数生命周期接口生命周期易管理的场景

内容的提问来源于stack exchange,提问作者Kirito

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:50:58