Delphi:测试事件处理程序分配

sta*_*005 8 delphi event-handling

我想在构造函数中分配一个事件处理程序,如果它没有分配.因此,我想删除析构函数中最终分配的事件处理程序.我编写的代码如下,但无法编译.

constructor TSomeControl.Create(Panel: TPanel);
begin
  inherited Create;
  FPanel := Panel;
  if not Assigned(FPanel.OnResize) then
    FPanel.OnResize := HandlePanelResize;
end;

destructor TSomeControl.Destroy;
begin
  if @FPanel.OnResize = @HandlePanelResize then // [dcc32 Error] E2036 Variable required
    FPanel.OnResize := nil;
  FPanel := nil;
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)

如何正确测试?我知道解决方案是使用变量来记录,无论我是否已分配OnResize.但我不希望这作为解决方案.

Ste*_*nke 12

无需在此处编写任何自定义代码,因为您可以使用以下现有的比较器Generics.Defaults:

destructor TSomeControl.Destroy;
begin
  if Assigned(FPanel) and TEqualityComparer<TNotifyEvent>.Default.Equals(
    FPanel.OnResize, HandlePanelResize) then
    FPanel.OnResize := nil;
  FPanel := nil;
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)


Dav*_*nan 5

这是OnResize属性而不是变量,这使情况变得复杂。在编译器不认为您想调用该方法的情况下,直接引用方法是非常困难的。这是Pascal方便的一大缺点,它允许您不使用parens来调用过程。

所有这一切使得很难以单线方式进行。据我所知,您将需要执行以下操作:

destructor TSomeControl.Destroy;
var
  Method1, Method2: TNotifyEvent;
begin
  if Assigned(FPanel) then
  begin
    Method1 := FPanel.OnResize;
    Method2 := HandlePanelResize;
    if TMethod(Method1) = TMethod(Method2) then
      FPanel.OnResize := nil;
  end;
  FPanel := nil;
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)

这依赖于现代Delphi的TMethod记录,其中包括重载的等于运算符来使=测试工作。

如果我不止一次地将其包装在通用方法中。它可能看起来像这样:

type
  TEventComparer = class
    class function Equal<T>(const lhs, rhs: T): Boolean; static;
  end;

class function TEventComparer.Equal<T>(const lhs, rhs: T): Boolean;
begin
  Assert(SizeOf(T)=SizeOf(TMethod));
  Result := TMethod((@lhs)^)=TMethod((@rhs)^);
end;
Run Code Online (Sandbox Code Playgroud)

您可以这样称呼它:

destructor TSomeControl.Destroy;
begin
  if Assigned(FPanel) and TEventComparer.Equal<TNotifyEvent>(FPanel.OnResize, 
    HandlePanelResize) then
    FPanel.OnResize := nil;
  FPanel := nil;
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)

突出显示的一件事是,您可以使用的常规约束不允许您将类型约束为方法指针。因此,基本的健全性检查应确保T方法的大小与方法的大小相同。但这并不能提供太多的安全性。您可以通过Int64或调用此方法Double。我很想知道是否有人可以提出一个更清洁的版本。