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

Delphi接口依赖注入容器实现补全:Get<I>方法疑问

问题:Delphi基于接口的依赖注入容器Get方法实现疑问

我正在实现一个Delphi中基于接口的简易依赖注入容器,仅从YouTube课程中学到了方法签名,不清楚具体实现细节。目前大部分代码已完成,但核心方法function Get<I: IInterface>: I;的实现有疑问,不知道如何从字典中获取对应的TFunc<IInterface>并转换为指定返回类型I。

完整容器代码

unit uContainerDefault;

interface

uses
  System.TypInfo,
  System.SysUtils,
  System.Generics.Collections;

type
  TGetter = TFunc<IInterface>;

  TContainer = class
  strict private
    FRecords: TDictionary<PTypeInfo, TGetter>;

    class var FDefault: TContainer;
    class function GetDefault: TContainer; static;

  public
    class constructor Create;
    class destructor Destroy;

    constructor Create;
    destructor Destroy; override;

    procedure Register<I: IInterface>(const pGetter: TGetter);

    function Get<I: IInterface>: I;

    class property Default: TContainer read GetDefault;
  end;

implementation

{ TContainer }

class constructor TContainer.Create;
begin
  FDefault := TContainer.Create;
end;

constructor TContainer.Create;
begin
  FRecords := TDictionary<PTypeInfo, TGetter>.Create;
end;

destructor TContainer.Destroy;
begin
  FRecords.Free;
  inherited;
end;

class destructor TContainer.Destroy;
begin
  FDefault.Free;
end;

function TContainer.Get<I>: I;
begin
  {通过这段代码,我可以从字典中获取能返回接口的"TFunc<IInterface>",但我不知道是否应该用它来生成返回结果。}
  FRecords.Items[TypeInfo(I)];
end;

class function TContainer.GetDefault: TContainer;
begin
  Result := FDefault;
end;

procedure TContainer.Register<I>(const pGetter: TGetter);
begin
  FRecords.Add(TypeInfo(I), pGetter);
end;

end.

注册示例(TProduct类初始化代码)

initialization
  TContainer.Default.Register<IProduct>(
    function: IInterface
    begin
      Result := TProduct.Create;
    end);

Get方法调用示例(窗体中操作Product实体)

begin
  TContainer.Default.Get<IProduct>.GetProduct;
end;

待实现的Get方法代码

function TContainer.Get<I>: I;
begin
  {通过这段代码,我可以从字典中获取能返回接口的"TFunc<IInterface>",但我不知道是否应该用它来生成返回结果。}
  FRecords.Items[TypeInfo(I)];
end;

解决方案

你需要取出字典中注册的TGetter并执行,将返回的IInterface实例转换为泛型参数指定的接口类型I,同时处理类型不匹配或未注册的情况。以下是完整的实现代码:

function TContainer.Get<I>: I;
var
  LGetter: TGetter;
  LIntf: IInterface;
begin
  // 尝试从字典中获取对应接口的Getter
  if not FRecords.TryGetValue(TypeInfo(I), LGetter) then
    raise Exception.CreateFmt('接口 %s 未注册', [GetTypeName(TypeInfo(I))]);

  // 执行Getter获取基础接口实例
  LIntf := LGetter();
  
  // 将基础接口转换为目标类型,同时验证兼容性
  if not Supports(LIntf, I, Result) then
    raise Exception.CreateFmt('注册的Getter未返回 %s 类型的实例', [GetTypeName(TypeInfo(I))]);
end;

代码说明

  • 使用TryGetValue替代直接访问Items,避免键不存在时抛出的默认异常,可根据需求调整错误处理(比如返回nil或自定义异常)
  • 执行LGetter()获取IInterface类型的实例
  • 通过Supports函数完成类型转换,同时验证实例是否确实实现了目标接口I,确保类型安全

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 10:53:12