4

我会在第二次提出这个问题。请不要怪我。

情况:

我有一个表格

TfrmMain = class(TForm)
private
   [Inject('IniFileSettings')]
   FSettings: ISettings;
public
end;

我有容器初始化程序:

procedure BuildContainer(const container: TContainer);
begin
  container.RegisterType<TIniSettings>.Implements<ISettings>('IniFileSettings');

  container.RegisterType<TfrmMain, TfrmMain>.DelegateTo(
    function: TfrmMain
    begin
      Application.CreateForm(TfrmMain, Result);
    end);

  container.Build;
end;

所以我通过容器初始化了 TfrmMain 和 TINiSettings。

在 .DPR 我有:

begin
  BuildContainer(GlobalContainer);
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TfrmMain, frmMain);
  Application.Run;
end.

我还有一个 TApplication 助手:

procedure TApplicationHelper.CreateForm(InstanceClass: TComponentClass; var Reference);
var
  locator: IServiceLocator;
begin
  locator := TServiceLocatorAdapter.Create(GlobalContainer);
  if locator.HasService(InstanceClass.ClassInfo) then
    TObject(Reference) := GlobalContainer.Resolve(InstanceClass.ClassInfo).AsObject
  else
    inherited CreateForm(InstanceClass, Reference);
end;

问题:当我尝试

procedure TfrmMain.FormCreate(Sender: TObject);
begin
   s := FSettings.ReadString('Connection', 'Server', 'localhost');
end;

我得到 AV 异常,因为 FSettings 目前是 NIL。

从容器中获取 FSettings 对象的正确方法是什么?

更新:

FSettings := GlobalContainer.Resolve<ISettings>;

这一行工作得很好......和上次一样,我在使用 [Inject] 属性时遇到了问题。即使使用 Stefan 的解决方案,我也可以使该方法起作用:

如何在 Spring4D GlobalContainer 中初始化主应用程序表单?

4

1 回答 1

6

首先,容器不再具有 HasService 的原因是该方法已被删除。您可以按如下方式访问它:

if container.Kernel.Registry.HasService(...) then  // yeah yeah, I know LoD is crying right now ;)

我会避免混合使用 ServiceLocator 和 GlobalContainer。虽然它们应该指向同一个实例,但可能并非如此,因为实际上有人可以将其中一个指向另一个实例。如果您真的想在这种情况下使用 ServiceLocator,那么也可以从 ServiceLocator 解析。但请记住,容器上没有任何东西不知道(即使您必须调用内核的某些不同部分。

但这不是您在设置注入时面临的问题。你遇到的问题是时机。FormCreate 方法(我只是猜测它附加到 OnCreate 事件)。因此容器实例化了 TfrmMain,事件被调用,然后返回到容器代码,容器代码随后执行所有注入。因此,在构造期间调用的某些代码中调用尚未通过构造函数注入的东西是一种时间耦合。

这个问题有不同的方法:

  • 将您对 FSettings 的访问权限移至稍后触发的某个事件(如 OnShow 或 OnActivate)
  • 不要使用字段注入它可能会很好,但是它将您的代码耦合到容器,因为“传统”代码无法做到这一点。使用属性注入和执行代码的设置器。

当您将构造函数注入视为强制性的依赖项,而将属性注入视为可选的依赖项时,我会说使用构造函数注入。但是知道您使用 TComponent 后代我可能会在这种情况下使用属性注入,尽管该依赖项不是可选的。

于 2014-10-23T21:49:28.257 回答