Home > other >  Spring4D - How to resolve from container with TComponent parameter to auto factory
Spring4D - How to resolve from container with TComponent parameter to auto factory

Time:05-10

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.

  • Related