Если я назначу процедуру для свойства объекта, может ли она ссылаться на другие свойства объекта? - PullRequest
0 голосов
/ 17 февраля 2020

Лучше всего показать на простом примере, вопрос - это 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 ]

4 голосов
/ 17 февраля 2020

Комментарий Питера показывает возможность. Нужна хитрость пересылки:

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;
3 голосов
/ 17 февраля 2020

Нет, это невозможно.

Хотя ExternalCaller - это метод - а TProcType - это тип метода ("объекта") - скрытый Self параметр для ExternalCaller() относится к объекту TFrmSelfRef, который создает lSomeObj; это не относится к lSomeObj.


Кроме того, вы, вероятно, уже знаете это, но никогда пишите

lSomeObj := 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;

Если возникает исключение (или вы выходите с помощью Exit или Break или Continue) - как обычно в Delphi - вы не должны пропускать память и другие ресурсы! Всегда используйте try..finally для защиты ресурсов.

1 голос
/ 17 февраля 2020

То, о чем вы просите, это возможно с какой-то уродливой 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;
...