Delphi 如何避免链式方法中出现未初始化的变量
Delphi how to avoid an uninitialized variable in chain methods
我正在尝试构建一个 API,但我遇到了以下问题:
这里是模拟问题的全部代码
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.
问题出在代码段
TMyManager.Create (). Produces (my) .Show (my.Name = 'Paul');
变量 my 没有初始化,因为代码片段
my.name = 'Paulo'
之前检查过
produce
我这样调用没有出现问题:
var
my: IMyEntity;
MyManager: IManager;
begin
MyManager: = TMyManager.Create (). Produces (my) as IManager;
MyManager.Show (my.Name = 'Paul 2');
因为在调用Show方法之前初始化了变量my
如何防止变量 my 在初始化之前被使用?
谁能帮我解决这个问题?非常感谢
代码还是太复杂了,但主要问题是
TMyManager.Create
产生一个对象(不是接口引用!)引用计数为0。如果你将它转换为正确的接口类型,它将获得 1 的引用计数:
(TMyManager.Create as IManager)
所以现在可以了:
procedure TForm1.btnNotWorkingClick(Sender: TObject);
var
my: IMyEntity;
begin
(TMyManager.Create as IManager).produce(my).Show(my.Nome = 'Paulo');
end;
并产生结果 'true'
.
这就是您修改后的代码有效的原因。您在调用 Show
之前将 Create
的结果分配给 IManager
,并且 produce
显然不会更改管理器的引用计数,而 Show
(间接地)确实如此。 我并没有试图找出方法 -- 你的代码非常复杂。
请注意,在 produce
有机会初始化 my
之前,不会调用 Show
,也不会评估其参数。这根本不是问题。
我正在尝试构建一个 API,但我遇到了以下问题:
这里是模拟问题的全部代码
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.
问题出在代码段
TMyManager.Create (). Produces (my) .Show (my.Name = 'Paul');
变量 my 没有初始化,因为代码片段
my.name = 'Paulo'
之前检查过
produce
我这样调用没有出现问题:
var
my: IMyEntity;
MyManager: IManager;
begin
MyManager: = TMyManager.Create (). Produces (my) as IManager;
MyManager.Show (my.Name = 'Paul 2');
因为在调用Show方法之前初始化了变量my
如何防止变量 my 在初始化之前被使用?
谁能帮我解决这个问题?非常感谢
代码还是太复杂了,但主要问题是
TMyManager.Create
产生一个对象(不是接口引用!)引用计数为0。如果你将它转换为正确的接口类型,它将获得 1 的引用计数:
(TMyManager.Create as IManager)
所以现在可以了:
procedure TForm1.btnNotWorkingClick(Sender: TObject);
var
my: IMyEntity;
begin
(TMyManager.Create as IManager).produce(my).Show(my.Nome = 'Paulo');
end;
并产生结果 'true'
.
这就是您修改后的代码有效的原因。您在调用 Show
之前将 Create
的结果分配给 IManager
,并且 produce
显然不会更改管理器的引用计数,而 Show
(间接地)确实如此。 我并没有试图找出方法 -- 你的代码非常复杂。
请注意,在 produce
有机会初始化 my
之前,不会调用 Show
,也不会评估其参数。这根本不是问题。