For an example I would like to resolve a class passing in a TComponent and TNotifyEvent such as below but the base constructor of TObject gets called and not of TMy.
GlobalContainer.RegisterType<TMy>;
GlobalContainer.RegisterFactory<Func<TComponent,TNotifyEvent,TMy>>(TParamResolution.ByType);
var F:=GlobalContainer.Resolve<Func<TComponent,TNotifyEvent,TMy>>;
F(Self,Self.OnActivate);
I can get around the issue by writing some very ugly code as below but think that this kind of resolution would be so common that I must be doing something wrong.
TOther = class
end;
TMy = class
public
constructor Create(C: TComponent; N: TNotifyEvent; O: TOther);
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
GlobalContainer.RegisterType<TOther>;
GlobalContainer.RegisterType<TMy>;
GlobalContainer.RegisterType<Func<TComponent,TNotifyEvent,TMy>>(
function: Func<TComponent,TNotifyEvent,TMy>
begin
Result:=Func<TComponent,TNotifyEvent,TMy>(
function(O: TComponent; N: TNotifyEVent): TMy
begin
Result:=TMy.Create(O,N,GlobalContainer.Resolve<TOther>);
end
);
end
);
GlobalContainer.Build;
var F:=GlobalContainer.Resolve<Func<TComponent,TNotifyEvent,TMy>>;
F(Self,Self.OnActivate);
end;
constructor TMy.Create(C: TComponent; N: TNotifyEvent; O: TOther);
begin
OutputDebugString('Resolved');
end;
Thanks in advance for any pointers.
CodePudding user response:
This should work now after the latest commit in develop.
The issue was the typed parameter resolution was bound to the argument type instead of the parameter type. In this case, this resulted in a typed value with type TForm2
(taken from the argument being passed) which did not match the type of the C argument of the constructor of type TComponent
because that matching checks for type identity and not assignment compatibility.
After the fix typed parameter resolution works exactly on the parameter types of the factory function instead of the possibly (in case of objects) more narrow actual type of the argument.
FWIW for future reference - when registering a factory manually it's usually not necessary to use RegisterType
providing the delegate but can directly use RegisterInstance
like so (keep in mind that Spring.Func<...> has
const` parameters) when it does not have any captured state:
GlobalContainer.RegisterInstance<Func<TComponent,TNotifyEvent,TMy>>(
function(const O: TComponent; const N: TNotifyEVent): TMy
begin
// ....
end);
Edit: I also added automatic detection for the best parameter resolution default. When the factory type is a Spring.Func<...>
it automatically uses ByType
so it can omitted from the RegisterFactory
call. For all other types it uses ByName
as default as before.