Object 如果将过程指定给对象属性,它是否可以引用该对象的其他属性?

Object 如果将过程指定给对象属性,它是否可以引用该对象的其他属性?,object,delphi,delphi-10.3-rio,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 TSomeO

通过一个简单的示例可以最好地显示,问题是底部的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实例,但是ExternalCaller引用了我不想链接(包含TSomeObj的单元)的其他对象/单元。

不,这是不可能的

尽管
ExternalCaller
是一种方法,
TProcType
是一种方法类型(“对象”),
ExternalCaller()
的隐藏
Self
参数是指创建
lSomeObj
TFrmSelfRef
对象;它不是指
lSomeObj


另外,你可能已经知道这一点,但从来没有写过

相反,写

lSomeObj := TSomeObj.Create;
try
  lSomeObj.Identifier := 200;
  lSomeObj.Caller := ExternalCaller;
  lSomeObj.Caller;
finally
  lSomeObj.Free;
end;
如果出现异常(或者您使用
退出
中断
继续
)离开)——这在Delphi中很正常——您不能泄漏内存和其他资源!始终使用
try..finally
保护资源。

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

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;

你所要求的可能是一些丑陋的
t方法
hackery,例如:

类型
TFrmSelfRef=类(TForm)
b测试:t按钮;
程序BtnTestClick(发送方:ToObject);
私有的
公众的
程序外部调用程序;
结束;
类型
TProcType=对象的过程;
类型
TSomeObj=类
私有的
FIdentifier:整数;
FCaller:TProcType;
公众的
属性调用方:TProcType read FCaller write FCaller;
属性标识符:整数读取标识符写入标识符;
结束;
...
程序TFrmSelfRef.BtnTestClick(发送方:TObject);
变量
lSomeObj:TSomeObj;
P:TProcType;
开始
lSomeObj:=TSomeObj.Create;
尝试
lSomeObj.标识符:=200;
//lSomeObj.Caller:=ExternalCaller;
P:=外部调用方;
t方法(P).数据:=lSomeObj;
lSomeObj.Caller:=P;
lSomeObj.呼叫者;
最后
lSomeObj.Free;
结束;
结束;
过程TFrmSelfRef.ExternalCaller;
开始
ShowMessage('标识符为'+IntToStr(TSomeObj(Self.Identifier));
结束;
但如果使用不当,这是非常危险的。我不推荐这种方法!我只是为了完整起见才介绍它

用彼得的解决方案代替

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

类型
TFrmSelfRef=类(TForm)
b测试:t按钮;
程序BtnTestClick(发送方:ToObject);
私有的
公众的
结束;
类型
TProcType=对象的过程;
类型
TSomeObj=类
私有的
FIdentifier:整数;
FCaller:TProcType;
公众的
程序外部调用程序;
属性调用方:TProcType read FCaller write FCaller;
属性标识符:整数读取标识符写入标识符;
结束;
...
程序TFrmSelfRef.BtnTestClick(发送方:TObject);
变量
lSomeObj:TSomeObj;
P:TProcType;
开始
lSomeObj:=TSomeObj.Create;
尝试
lSomeObj.标识符:=200;
lSomeObj.Caller:=lSomeObj.ExternalCaller;
lSomeObj.呼叫者;
最后
lSomeObj.Free;
结束;
结束;
程序TSomeObj.ExternalCaller;
开始
ShowMessage('标识符为'+IntToStr(标识符));
结束;

@jandogen:当然,你可以使用各种变通方法和黑客。也许其他人能想出一些聪明的办法。(但它值得吗?黑客使代码可读性降低。)
Sender
参数是否是一个选项
tprotype=对象的过程(发送方:TSomeObj)
。是的,这是最自然的解决方案。(我在最初的评论中提出了这个建议,但我认为您反对对象之间的这种显式连接。)请注意,这基本上是一个类型化的TNotifyEvent。正如Andreas所提到的,这是一种非常惯用的方法。这个答案使问题看起来像是询问是否可以将参数传递给过程。。。。安德烈亚斯删除的答案是正确的,我同意…@Sertac。我真的不明白这是怎么回答这个问题的。是的,我同意这个诡计——我不会那么做。但将ExternalCaller()移动到TSomeObj是我试图阻止的……事实上,这也是我想到的第一个黑客。在某些情况下,它实际上可能是一个好的解决方案(我在自己的代码中使用过一次),但大多数情况下,它可能不是。
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;