我想在构造函数中分配一个事件处理程序,如果它没有分配。因此,我想删除析构函数中最终分配的事件处理程序。我编写的代码如下,但无法编译。
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
。但我不希望这作为解决方案。
答案 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
的大小与方法的大小相同。但这并没有提供太多安全性。您可以通过Int64
或Double
调用此方法。我有兴趣看看是否有人能想出一个更清洁的变体。
答案 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;