测试事件处理程序赋值



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

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

这里不需要编写任何自定义代码,因为您可以使用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;

OnResize是一个属性而不是一个变量,这使得情况更加复杂。直接引用一个方法而不让编译器认为你想调用这个方法是很困难的。这是Pascal的一大缺点,它允许您在不使用父类的情况下调用过程。

所有这些使得在一行代码中完成它相当困难。在我看来,您需要做这样的事情:

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来调用这个方法。我很想看看是否有人能想出一个更简洁的版本

根本不需要使用Generics.Defaults或任何泛型。在System单元中声明了TMethod记录,所以这可能是最简单的一个:

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

相关内容

  • 没有找到相关文章

最新更新