在 Spring4D 中调用 GlobalContainer.Resolve 时如何将字符串值作为参数传递?
How can I pass a string value as a parameter when calling GlobalContainer.Resolve in Spring4D?
使用Spring4D时,如何在调用GlobalContainer时将字符串值作为参数传递。解析以便在已解析的 class 构造函数中使用此字符串值?
我想解析映射到 TWorker 的 class IWorker。 TWorker class 在其构造函数中依赖于 ITool 以及工人姓名的字符串。
我猜答案在于可以作为参数给 GlobalContainer.Resolve 的 TValue 数组,但我不明白如何使用它。
我发现这个 post 关于在调用 GlobalContainer.Resolve 时使用 TParameterOverride 作为参数可能有用,但这个功能似乎在 1.1 版的 Spring4D 中消失了。
我想在注册我的类型时避免调用 InjectConstructor。
我需要帮助的部分是
GlobalContainer.Resolve<IWorker>([{what do I put here?}]).Work;
这是我的一个小项目
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.
帮助将不胜感激。
谢谢!
通常的解决方案是在容器中为工人注册一个工厂,然后要求工厂return使用特定工具和字符串(名称?)的工人。
您当前的代码看起来可能希望在应用程序内部使用容器,这是一种味道,因为容器应该只在 Composition root 中使用,实际上
我知道应该避免这种情况,但我找到了一种使用字符串参数调用 resolve 的方法。
以下代码有效,但不是一个好主意:
GlobalContainer.RegisterType<ITool, THammer>;
GlobalContainer.RegisterInstance<TFunc<string, IWorker>>(
function(workerName: string): IWorker
begin
Result := TWorker.Create(GlobalContainer.Resolve<ITool>, workerName);
end);
GlobalContainer.Build;
GlobalContainer.Resolve<TFunc<string, IWorker>>.Invoke('Bob').Work;
正如 Sam 所说,您应该避免在整个代码中使用容器作为服务定位器,因为这只是将构造函数调用替换为对容器的调用,这会导致比所有硬连线更糟糕的代码。
虽然 可以将参数传递给 Resolve 调用,但它确实应该通过使用工厂来解决。
这将是如何传递名称参数的值(该工具由容器注入,因为它知道这一点(TNamedValue
在 Spring.pas
中声明)。
GlobalContainer.Resolve<IWorker>([TNamedValue.Create('name', 'Bob')]).Work;
但是我们可以将该代码与注册工厂结合起来(不幸的是,因为 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;
因此,这使您可以在代码中的某处放置一个 TWorkerFactory
参数,然后容器可以将其注入。这样你就使用依赖注入解耦了代码,但没有对容器的任何直接依赖(事实上你仍然可以手动连接所有东西,这是我之前所说的规则)
随着 1.2 版本的发布,容器将支持自动创建工厂,因此您可以编写如下代码:
type
{$M+}
TWorkerFactory = reference to function(const name: string): IWorker;
...
GlobalContainer.RegisterFactory<TWorkerFactory>;
这会自动创建一个代理,将工厂方法的参数进一步传递到容器中。
使用Spring4D时,如何在调用GlobalContainer时将字符串值作为参数传递。解析以便在已解析的 class 构造函数中使用此字符串值?
我想解析映射到 TWorker 的 class IWorker。 TWorker class 在其构造函数中依赖于 ITool 以及工人姓名的字符串。
我猜答案在于可以作为参数给 GlobalContainer.Resolve 的 TValue 数组,但我不明白如何使用它。
我发现这个 post 关于在调用 GlobalContainer.Resolve 时使用 TParameterOverride 作为参数可能有用,但这个功能似乎在 1.1 版的 Spring4D 中消失了。
我想在注册我的类型时避免调用 InjectConstructor。
我需要帮助的部分是
GlobalContainer.Resolve<IWorker>([{what do I put here?}]).Work;
这是我的一个小项目
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.
帮助将不胜感激。 谢谢!
通常的解决方案是在容器中为工人注册一个工厂,然后要求工厂return使用特定工具和字符串(名称?)的工人。
您当前的代码看起来可能希望在应用程序内部使用容器,这是一种味道,因为容器应该只在 Composition root 中使用,实际上
我知道应该避免这种情况,但我找到了一种使用字符串参数调用 resolve 的方法。
以下代码有效,但不是一个好主意:
GlobalContainer.RegisterType<ITool, THammer>;
GlobalContainer.RegisterInstance<TFunc<string, IWorker>>(
function(workerName: string): IWorker
begin
Result := TWorker.Create(GlobalContainer.Resolve<ITool>, workerName);
end);
GlobalContainer.Build;
GlobalContainer.Resolve<TFunc<string, IWorker>>.Invoke('Bob').Work;
正如 Sam 所说,您应该避免在整个代码中使用容器作为服务定位器,因为这只是将构造函数调用替换为对容器的调用,这会导致比所有硬连线更糟糕的代码。
虽然 可以将参数传递给 Resolve 调用,但它确实应该通过使用工厂来解决。
这将是如何传递名称参数的值(该工具由容器注入,因为它知道这一点(TNamedValue
在 Spring.pas
中声明)。
GlobalContainer.Resolve<IWorker>([TNamedValue.Create('name', 'Bob')]).Work;
但是我们可以将该代码与注册工厂结合起来(不幸的是,因为 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;
因此,这使您可以在代码中的某处放置一个 TWorkerFactory
参数,然后容器可以将其注入。这样你就使用依赖注入解耦了代码,但没有对容器的任何直接依赖(事实上你仍然可以手动连接所有东西,这是我之前所说的规则)
随着 1.2 版本的发布,容器将支持自动创建工厂,因此您可以编写如下代码:
type
{$M+}
TWorkerFactory = reference to function(const name: string): IWorker;
...
GlobalContainer.RegisterFactory<TWorkerFactory>;
这会自动创建一个代理,将工厂方法的参数进一步传递到容器中。