【问题标题】:How to check if two method references are referencing same method?如何检查两个方法引用是否引用相同的方法?
【发布时间】:2016-10-26 11:18:06
【问题描述】:

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

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 的结构但用于方法引用?

【问题讨论】:

  • TEqualityComparer.Default.Equals(A, B)
  • 您可以使用 TProc 代替您自己的声明...只需添加 System.SysUtils。
  • @ZENsas 我知道 TProc,我只是试图让示例尽可能清晰。 TEqualityComparer.Default.Equals(A, B) 不起作用,我只是测试了一下(否则 TList.Remove 也会起作用,它基于默认比较器)。
  • @ZENsan TList&lt;T&gt; 不使用 TEqualityComparer&lt;T&gt;TComparer&lt;T&gt; 但这并不重要,因为它们都失败了,因为它们只是比较引用(指针)。正如我在回答中解释的那样,传递给 AddRemove 的引用是不同的。
  • @ZENsan 你没抓住重点。该列表已使用默认比较器。它认为这两种匿名方法是不同的,因为作为匿名方法,它们是不同的。

标签: delphi anonymous-methods method-reference


【解决方案1】:

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

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

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

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

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

这意味着传递给AddRemove 的值是不同的,即使它们的主体中的代码是相同的——即使绕过它也需要二进制代码分析来确定主体中的代码是否是一样的。

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

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;

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

【讨论】:

  • 我没想到当我以方法引用作为参数调用某些东西时会生成匿名方法。我想我实际上可以在没有匿名方法的情况下使用方法引用。谢谢!
  • @AndreiGalatyn:您可以在没有匿名方法的情况下使用procedure of object 引用,但您不能在没有匿名方法的情况下使用reference to procedure 引用,因为后者匿名方法引用。
猜你喜欢
  • 1970-01-01
  • 2012-11-21
  • 2010-11-24
  • 1970-01-01
  • 2014-06-05
  • 1970-01-01
  • 2023-02-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多