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

时间:2014-06-08 16:17:15

标签: 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;

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

3 个答案:

答案 0 :(得分: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;

答案 1 :(得分: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;

这依赖于现代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;

您可以这样称呼它:

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

这一点强调的是,您可用的通用约束不允许您将类型约束为方法指针。因此,基本的健全性检查T的大小与方法的大小相同。但这并没有提供太多安全性。您可以通过Int64Double调用此方法。我有兴趣看看是否有人能想出一个更清洁的变体。

答案 2 :(得分:1)

根本不需要使用Generics.Defaults或任何泛型。在TMethod单位中声明了System条记录,因此这可能是最简单的记录:

destructor TSomeControl.Destroy;
var
  Event: TNotifyEvent;
begin
  Event := HandlePanelResize;
  if TMethod(FPanel.OnResize).Code = Addr(Event) then
    FPanel.OnResize := nil;
  FPanel := nil;
  inherited;
end;