delphi - 如何检查两个方法引用是否引用同一个方法?

标签 delphi anonymous-methods method-reference

我正在尝试创建事件处理程序列表,其中处理程序是方法引用。 要删除特定的处理程序,我需要在列表中找到它。 但是我如何比较两个方法引用的代码地址?

type
  TEventHandler = reference to procedure;

procedure TestProc;
begin
end;

procedure TForm26.FormCreate(Sender: TObject);
var
  Handlers: TList<TEventHandler>;
begin
  Handlers := TList<TEventHandler>.create;
  try
    Handlers.Add(TestProc);
    Handlers.Remove(TestProc); { doesn't work }
    Assert(Handlers.Count=0);  { fails }
    Assert(Handlers.IndexOf(TestProc)>=0); { fails }
  finally
    FreeAndNil(Handlers);
  end;
end;

TList<> 的默认比较器无法正确比较方法引用。 我如何比较它们?是否有类似于 TMethod 的结构,但用于方法引用?

最佳答案

这并不像看起来那么容易。

要理解为什么会发生这种情况,您需要了解编译器如何执行对方法引用的分配。

你写的代码基本上被编译器翻译成这样:

Handlers.Add(procedure begin TestProc; end);
Handlers.Remove(procedure begin TestProc; end);

现在我们必须知道,如果同一例程中有多个匿名方法,即使它们的代码相同,它们实际上也是不同的匿名方法。 (参见How are anonymous methods implemented under the hood?)

这意味着传递给 AddRemove 的值是不同的,即使它们主体中的代码相同 - 即使进行黑客攻击也需要二进制代码分析以确定body内部的代码是否相同。

如果您按如下方式更改代码,它会起作用,因为这样您只有一个匿名方法 - 对于这个片段,它可以起作用,但通常您不会在完全相同的例程中添加和删除:

var
  Handlers: TList<TEventHandler>;
  Handler: TEventHandler;
begin
  Handlers := TList<TEventHandler>.create;
  try
    Handler := TestProc;
    Handlers.Add(Handler);
    Handlers.Remove(Handler);
    Assert(Handlers.Count=0);
  finally
    FreeAndNil(Handlers);
  end;
end;

如果您想要一个添加和删除事件处理程序的列表,我个人的建议是避免匿名方法类型并使用过程或方法:

type
  TEventHandlerA = procedure;
  TEventHandlerB = procedure of object;

哪个更好取决于您,因为您更了解自己的代码。

关于delphi - 如何检查两个方法引用是否引用同一个方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40260923/

相关文章:

java - 是否有类似于 Integer::sum 的乘法?

java - 如何使用 Groovy 生成用于测试目的的 Java 方法引用

delphi - 我的自定义控件闪烁。是什么原因造成的以及如何消除它?

java - 匿名方法使用泛型值作为参数

java - 实例方法引用和 Lambda 参数

java - 为什么参数定义方法匿名内部类的方法内部行有效?

c# - C# 事件处理程序委托(delegate)中的闭包?

delphi - 这里有内存泄漏吗?

delphi - CreateProcess .. WaitForSingleObject .. CloseHandle 调用的最佳 try..finally 放置

database - 在 DbNavigator Delphi 上按下刷新后的错误结果