I want to assign an event handler in constructor, if it does not have one assigned. Consequentially I want to remove the eventually assigned event handler in destructor. I wrote the code as follows, but cannot be compiled.
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;
How to test it properly? I know a solution is to use a variable to record, whether I have assigned OnResize. But I do not want this as solution.
No need to write any custom code here as you can use the already existing comparers from 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;
                        This is complicated by the fact that OnResize is a property rather than a variable. And it's quite hard to refer to a method directly without the compiler thinking you want to call the method. This is the big drawback of Pascal's convenience of allowing you to call a procedure without using parens. 
All this makes it rather hard to do it in a one-liner. As far as I can see you will need to do something like this:
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;
This relies on modern Delphi's TMethod record which includes an overloaded equality operator to make the = test work. 
I would wrap this all up in a generic method if I was doing it more than once. It might look like this:
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;
You'd call it like this:
destructor TSomeControl.Destroy;
begin
  if Assigned(FPanel) and TEventComparer.Equal<TNotifyEvent>(FPanel.OnResize, 
    HandlePanelResize) then
    FPanel.OnResize := nil;
  FPanel := nil;
  inherited;
end;
One thing that this highlights is that the generic constraints available to you do not allow you to constraint a type to being a method pointer. Hence the basic sanity check that the size of T is the same as the size of a method. This doesn't offer much safety though. You can call this method passing Int64, or Double. I'd be interested to see if anyone can come up with a cleaner variant.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With