I'm trying to build an API, but I'm stopped at the following problem:
Here is all the code to simulate the problem
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Rtti;
type
TForm1 = class(TForm)
btnWorking: TButton;
btnNotWorking: TButton;
procedure btnWorkingClick(Sender: TObject);
procedure btnNotWorkingClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TEntityManagerWhereExpression = record
FCondition: Boolean;
end;
TEntityManagerWhereExpressionFnc = reference to function(): TEntityManagerWhereExpression;
TEntityExpressionOperation = record
private
FCondition: Boolean;
public
class operator Implicit(A: TEntityExpressionOperation): TEntityManagerWhereExpression;
class operator Equal(A, B: TEntityExpressionOperation): TEntityExpressionOperation;
class operator BitwiseAnd(A, B: TEntityExpressionOperation): TEntityExpressionOperation;
end;
TEntityExpression<T> = record
private
FValue: TValue;
public
class operator Implicit(A: string): TEntityExpression<T>;
class operator Implicit(A: TEntityExpression<T>): string;
class operator Equal(A, B: TEntityExpression<T>): TEntityExpressionOperation;
end;
IMyEntity = interface
['{BE3EE808-47BB-4E83-94BA-ABC9B3D861F2}']
function GetNome: TEntityExpression<string>;
property Nome: TEntityExpression<string> read GetNome;
end;
TMyEntity = class(TInterfacedObject, IMyEntity)
function GetNome: TEntityExpression<string>;
end;
IManager = interface
['{DE38CC69-069F-48F9-A9A4-E93BA0A87E5F}']
function produce(out myEntity: IMyEntity): IManager;
procedure Show(const exp: TEntityManagerWhereExpression);
end;
TMyManager = class(TInterfacedObject, IManager)
public
function produce(out myEntity: IMyEntity): IManager;
procedure Show(const exp: TEntityManagerWhereExpression);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnNotWorkingClick(Sender: TObject);
var
my: IMyEntity;
begin
TMyManager.Create().produce(my).Show(my.Nome = 'Paulo');
end;
procedure TForm1.btnWorkingClick(Sender: TObject);
var
my: IMyEntity;
MyManager: IManager;
begin
MyManager := TMyManager.Create().produce(my) as IManager;
MyManager.Show(my.Nome = 'Paulo 2');
end;
{ TMyManager }
function TMyManager.produce(out myEntity: IMyEntity): IManager;
begin
Result := Self;
myEntity := TMyEntity.Create;
end;
procedure TMyManager.Show(const exp: TEntityManagerWhereExpression);
begin
if exp.FCondition then
ShowMessage('true')
else
ShowMessage('false');
end;
{ TEntityExpressionOperation }
class operator TEntityExpressionOperation.BitwiseAnd(A, B: TEntityExpressionOperation): TEntityExpressionOperation;
begin
Result.FCondition := A.FCondition and B.FCondition;
end;
class operator TEntityExpressionOperation.Equal(A, B: TEntityExpressionOperation): TEntityExpressionOperation;
begin
Result.FCondition := False;
end;
class operator TEntityExpressionOperation.Implicit(A: TEntityExpressionOperation): TEntityManagerWhereExpression;
begin
Result.FCondition := A.FCondition;
end;
{ TEntityExpression<T> }
class operator TEntityExpression<T>.Equal(A, B: TEntityExpression<T>): TEntityExpressionOperation;
begin
Result.FCondition := A.FValue.AsString = B.FValue.AsString;
end;
class operator TEntityExpression<T>.Implicit(A: string): TEntityExpression<T>;
begin
Result.FValue := A;
end;
class operator TEntityExpression<T>.Implicit(A: TEntityExpression<T>): string;
begin
Result := A.FValue.AsString;
end;
{ TMyEntity }
function TMyEntity.GetNome: TEntityExpression<string>;
begin
Result := 'Paulo';
end;
end.
The problem is in the code snippet
TMyManager.Create (). Produces (my) .Show (my.Name = 'Paul');
where the variable my was not initialized, since the code snippet
my.name = 'Paulo'
is checked before
produce
the problem does not occur when I call it this way:
var
my: IMyEntity;
MyManager: IManager;
begin
MyManager: = TMyManager.Create (). Produces (my) as IManager;
MyManager.Show (my.Name = 'Paul 2');
because the variable my is initialized before calling the Show method
how can I prevent the variable my from being used before it is initialized?
Can someone help me solve this problem? thank you so much
The code is still far too complex, but the main problem is that
TMyManager.Create
produces an object (not an interface reference!) with a reference count of 0. If you cast it to the proper interface type, it will get a reference count of 1:
(TMyManager.Create as IManager)
So now this works:
procedure TForm1.btnNotWorkingClick(Sender: TObject);
var
my: IMyEntity;
begin
(TMyManager.Create as IManager).produce(my).Show(my.Nome = 'Paulo');
end;
and produces the result 'true'
.
That is why your modified code works. You assign the result of Create
to an IManager
before you call Show
, and produce
obviously doesn't change the reference count for the manager, while Show
(indirectly) does. I didn't try to find out how -- your code is pretty convoluted.
Note that Show
is not called, nor its parameter evaluated, before produce
gets the chance to initialize my
. That is not the problem at all.