- changed status to resolved
Factory calls return nil instead of a service
Regarding latest commit in dev branch, tested in Delphi XE3 55b721319ed409024360d4960c68ac04e9d8fde8
If you call a newly introduced factory it calls the implementation constructor, however, it returns nil. It might be related to the VTable hack and interference counting.
The following test program demonstrates the issue:
program SpringFactoriesOfficial;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Spring.Container,
Spring.Container.Common;
type
ITestInterface = interface
['{A2BC7208-5933-41AC-A56E-5F7D1268F8B0}']
procedure DoSomething;
end;
ITestDependency = interface
['{25C6D7D5-C806-4A2D-8FE5-58FFCB21798C}']
procedure DoSomething;
end;
ITestFactory = interface( IFactory<ITestDependency, ITestInterface> )
end;
TTestInterfaceImpl = class(TInterfacedObject, ITestInterface)
strict private
FTestDependency: ITestDependency;
procedure DoSomething;
public
constructor Create(ATestDependency: ITestDependency);
end;
TTestDependencyImpl = class(TInterfacedObject, ITestDependency)
strict private
procedure DoSomething;
end;
procedure Main;
var
Container: TContainer;
Factory: ITestFactory;
Tester: ITestInterface;
Dependency: ITestDependency;
begin
Container := TContainer.Create;
with Container do begin
// Register
RegisterType<ITestInterface, TTestInterfaceImpl>;
RegisterType<ITestDependency, TTestDependencyImpl>;
RegisterType<ITestFactory>
.AsFactory;
// Build
Build;
// Resolve
Dependency := Resolve<ITestDependency>;
Factory := Resolve<ITestFactory>();
// Testing:
Tester := Factory(Dependency);
// output shows: constructor is called, however, result is of Factory(...) is nil
// Exception: AV because Tester is nil
Tester.DoSomething;
end;
end;
{ TTestInterfaceImpl }
constructor TTestInterfaceImpl.Create(ATestDependency: ITestDependency);
begin
FTestDependency := ATestDependency;
if not Assigned(FTestDependency) then
raise Exception.Create('Missing dependency: ATestDependency');
Writeln('TestInterface created!');
end;
procedure TTestInterfaceImpl.DoSomething;
begin
Writeln('Calling dependend interface:');
FTestDependency.DoSomething;
end;
{ TTestDependencyImpl }
procedure TTestDependencyImpl.DoSomething;
begin
Writeln('- TestDependency: Hello world! -');
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
I did not dig deeper into the working principle of the VTable hack and why it is required. (Stefan, you only mentioned that you needed it in the google groups discussion).
As I did my own implementation of TVirtualInterface factories I first had issue with the assignement of the virtual interface as service type without the "as" operator (as type info is only available at runtime). If this is why the cast into an empty interface with modified VTables is done you might also consider the following implementation as an alternative:
type
TFactoryVirtualInterface = class(TVirtualInterface)
...
/// <summary>
/// Provides a way to query the object as a given interface.
/// </summary>
/// <typeparam name="I">
/// the interface one wants to get served
/// </typeparam>
function CastAs<I: IInterface>: I;
...
end;
implementation
function TFactoryVirtualInterface.CastAs<I>: I;
begin
QueryInterface(GetTypeData(TypeInfo(I)).GUID, Result);
end;
Comments (1)
-
repo owner - Log in to comment
fixed issue
#26→ <<cset 85eb48b50254>>