如果我将过程分配给对象属性,它可以引用该对象的其他属性吗?

时间:2020-02-17 12:52:01

标签: object delphi delphi-10.3-rio

用一个简单的例子可以最好地显示,问题是底部的ShowMessage:

type
  TFrmSelfRef = class(TForm)
    BtnTest: TButton;
    procedure BtnTestClick(Sender: TObject);
  private
  public
    procedure ExternalCaller;
  end;

type
   TProcType = procedure of Object;

type
   TSomeObj = class
   private
      FIdentifier: Integer;
      FCaller    : TProcType;
   public
      property Caller     : TProcType read FCaller write FCaller;
      property Identifier : integer read FIdentifier write FIdentifier;
   end;

[snip]

procedure TFrmSelfRef.BtnTestClick(Sender: TObject);
var
   lSomeObj: TSomeObj;
begin
   lSomeObj := TSomeObj.Create;
   lSomeObj.Identifier := 200;
   lSomeObj.Caller := ExternalCaller;
   lSomeObj.Caller;
   lSomeObj.Free;
end;

procedure TFrmSelfRef.ExternalCaller;
begin
   ShowMessage('Can I access lSomeObj.Identifier (value:200) here?');
end;

原因:我已经有一个TSomeObj实例,其中包含Caller所需的所有信息,但是ExternalCaller引用了我不想链接(包含TSomeObj的其他对象/单元)。

3 个答案:

答案 0 :(得分:4)

Peter's comment显示了一种可能性。它需要一些转发技巧:

type
  TSomeObj = class; 

  TFrmSelfRef = class(TForm)
    BtnTest: TButton;
    procedure BtnTestClick(Sender: TObject);
  private
  public
    procedure ExternalCaller(ASomeObj: TSomeObj);
  end;

  TProcType = procedure(Sender: TSomeObj) of Object;

  TSomeObj = class
  private
    FIdentifier: Integer;
    FCaller    : TProcType;
  public
    property Caller     : TProcType read FCaller write FCaller;
    property Identifier : integer read FIdentifier write FIdentifier;
  end;

[snip]

procedure TFrmSelfRef.BtnTestClick(Sender: TObject);
var
  lSomeObj: TSomeObj;
begin
  lSomeObj := TSomeObj.Create;
  lSomeObj.Identifier := 200;
  lSomeObj.Caller := ExternalCaller;
  lSomeObj.Caller(lSomeObj);
end;

procedure TFrmSelfRef.ExternalCaller(ASomeObj: TSomeObj);
begin
  ShowMessage('I can access TSomeObj here! Identifier property value: ' + IntToStr(ASomeObj.Identifier));
end;

答案 1 :(得分:3)

不,那不可能。

尽管ExternalCaller是一个方法-TProcType是一个方法类型(“对象”),但{{ 1}}是指创建Self的{​​{1}}对象;它不引用ExternalCaller()


此外,您可能已经知道这一点,但是从不

TFrmSelfRef

相反,写

lSomeObj

如果发生异常(或者您通过使用lSomeObjlSomeObj := TSomeObj.Create; lSomeObj.Identifier := 200; lSomeObj.Caller := ExternalCaller; lSomeObj.Caller; lSomeObj.Free; lSomeObj := TSomeObj.Create; try lSomeObj.Identifier := 200; lSomeObj.Caller := ExternalCaller; lSomeObj.Caller; finally lSomeObj.Free; end; 离开)-就像在Delphi中一样-一定不要泄漏内存和其他资源! 始终使用Exit保护资源。

答案 2 :(得分:1)

您要的是可能并且带有一些丑陋的TMethod黑客,例如:

type
  TFrmSelfRef = class(TForm)
    BtnTest: TButton;
    procedure BtnTestClick(Sender: TObject);
  private
  public
    procedure ExternalCaller;
  end;

type
  TProcType = procedure of object;

type
  TSomeObj = class
  private
    FIdentifier: Integer;
    FCaller : TProcType;
  public
    property Caller : TProcType read FCaller write FCaller;
    property Identifier : Integer read FIdentifier write FIdentifier;
  end;

...

procedure TFrmSelfRef.BtnTestClick(Sender: TObject);
var
  lSomeObj: TSomeObj;
  P: TProcType;
begin
  lSomeObj := TSomeObj.Create;
  try
    lSomeObj.Identifier := 200;

    //lSomeObj.Caller := ExternalCaller;
    P := ExternalCaller;
    TMethod(P).Data := lSomeObj;
    lSomeObj.Caller := P;

    lSomeObj.Caller;
  finally
    lSomeObj.Free;
  end;
end;

procedure TFrmSelfRef.ExternalCaller;
begin
  ShowMessage('The Identifier is ' + IntToStr(TSomeObj(Self).Identifier));
end;

但是,如果滥用,这将非常危险。我不推荐这种方法!我只是出于完整性考虑而提出。

改为使用彼得的解决方案。

另一种解决方案是将ExternalCaller()移至TSomeObj,例如:

type
  TFrmSelfRef = class(TForm)
    BtnTest: TButton;
    procedure BtnTestClick(Sender: TObject);
  private
  public
  end;

type
  TProcType = procedure of object;

type
  TSomeObj = class
  private
    FIdentifier: Integer;
    FCaller : TProcType;
  public
    procedure ExternalCaller;
    property Caller : TProcType read FCaller write FCaller;
    property Identifier : Integer read FIdentifier write FIdentifier;
  end;

...

procedure TFrmSelfRef.BtnTestClick(Sender: TObject);
var
  lSomeObj: TSomeObj;
  P: TProcType;
begin
  lSomeObj := TSomeObj.Create;
  try
    lSomeObj.Identifier := 200;
    lSomeObj.Caller := lSomeObj.ExternalCaller;
    lSomeObj.Caller;
  finally
    lSomeObj.Free;
  end;
end;

procedure TSomeObj.ExternalCaller;
begin
  ShowMessage('The Identifier is ' + IntToStr(Identifier));
end;