在Spring4D中调用GlobalContainer.Resolve时,如何将字符串值作为参数传递?

Set*_*384 3 delphi dependency-injection ioc-container spring4d

使用Spring4D时,如何在调用GlobalContainer时将字符串值作为参数传递.解析,以便在已解析的类构造函数上使用此字符串值?

我想解决映射到TWorker的类IWorker.TWorker类在它的构造函数中依赖于ITool加上工作者名称的字符串.

我猜的答案在于TValue数组,它可以作为GlobalContainer.Resolve的参数给出,但我不明白如何使用它.

我发现这篇文章关于在调用可能有效的GlobalContainer.Resolve时使用TParameterOverride作为参数,但这个功能似乎已经在Spring4D的1.1版本中消失了.

我想在注册我的类型时避免调用InjectConstructor.

我需要帮助的部分是

GlobalContainer.Resolve<IWorker>([{what do I put here?}]).Work;
Run Code Online (Sandbox Code Playgroud)

这是我的一个小项目

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Spring.Container;

type
   IWorker = interface
   ['{2BBD7E9C-4806-4F01-9B05-9E9DD928D21D}']
      procedure Work;
   end;

   ITool = interface
   ['{F962209D-4BC3-41C4-9089-0A874632ED1A}']
      procedure Use;
   end;

   TWorker = class(TInterfacedObject, IWorker)
   private
      FTool: ITool;
      FName: string;
      procedure Work;
   public
      constructor Create(tool: ITool; name: string);
   end;

   THammer = class(TInterfacedObject, ITool)
   private
      procedure Use;
   end;

{ TWorker }
constructor TWorker.Create(tool: ITool; name: string);
begin
   FTool := tool;
   FName := name;
end;

procedure TWorker.Work;
begin
   Writeln(FName + ' is working');
   FTool.Use;
end;


{ THammer }
procedure THammer.Use;
begin
   Writeln('Using a hammer');
end;


begin
   try
      GlobalContainer.RegisterType<ITool, THammer>;
      GlobalContainer.RegisterType<IWorker, TWorker>; // TWorker constructor = Create(tool: ITool; name: string);
      GlobalContainer.Build;

      GlobalContainer.Resolve<IWorker>([{what do I put here?}]).Work;
      GlobalContainer.Resolve<IWorker>(['THammer.Create', 'Bob']).Work; //--> 'Unsatisfied constructor on type: TWorker'
      GlobalContainer.Resolve<IWorker>([THammer.Create, 'Bob']).Work; //--> Access violation
      GlobalContainer.Resolve<IWorker>([nil, 'Bob']).Work; //--> 'Unsatisfied constructor on type: TWorker'
      Readln;
   except
      on E: Exception do
      begin
         Writeln(E.ClassName, ': ', E.Message);
         Readln;
      end;
   end;
end.
Run Code Online (Sandbox Code Playgroud)

帮助将不胜感激.谢谢!

Ste*_*nke 6

正如Sam所说,你应该避免在整个代码中使用容器作为服务定位器,因为这只是对容器调用的构造函数调用的替代,这导致代码比硬连接的所有代码更糟糕.

虽然它可能的参数传递给resolve调用它真的应该利用工厂来解决.

这将是如何为name参数传递一个值(该工具由容器注入,因为它知道它(TNamedValue在声明中Spring.pas).

GlobalContainer.Resolve<IWorker>([TNamedValue.Create('name', 'Bob')]).Work;
Run Code Online (Sandbox Code Playgroud)

但是我们可以将这些代码与注册工厂结合起来(遗憾的是因为RTTI缺少关于类型的信息,我们必须使用匿名方法类型TFunc<...>)

type
  TWorkerFactory = TFunc<string, IWorker>;

...

GlobalContainer.RegisterType<ITool, THammer>;
GlobalContainer.RegisterType<IWorker, TWorker>;
GlobalContainer.RegisterInstance<TWorkerFactory>(
  function (name: string): IWorker
  begin
    Result := GlobalContainer.Resolve<IWorker>([TNamedValue.Create('name', name)]);
  end);
GlobalContainer.Build;

GlobalContainer.Resolve<TWorkerFactory>.Invoke('Bob').Work;
Run Code Online (Sandbox Code Playgroud)

因此,这使您可以TWorkerFactory在代码中的某处放置一个参数,然后容器可以注入它.这样你就可以使用依赖注入来解耦代码,但是没有任何直接依赖于容器(实际上你仍然可以手动连接所有内容,这是我之前所说的规则)

在1.2版本中,容器将支持自动工厂创建,因此您可以编写如下代码:

type
  {$M+}
  TWorkerFactory = reference to function(const name: string): IWorker;

...

GlobalContainer.RegisterFactory<TWorkerFactory>;
Run Code Online (Sandbox Code Playgroud)

这会自动创建一个代理,将工厂方法的参数进一步传递到容器中.