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,也不会评估其参数。这根本不是问题。