Delegate.Combine:如何检查多播(可组合)委托中是不是已经有委托?

Posted

技术标签:

【中文标题】Delegate.Combine:如何检查多播(可组合)委托中是不是已经有委托?【英文标题】:Delegate.Combine: How to Check if multicast (combinable) delegates already has a delegate inside of it?Delegate.Combine:如何检查多播(可组合)委托中是否已经有委托? 【发布时间】:2022-01-17 08:39:11 【问题描述】:

我正在使用 Unity 3D,但是,该信息对于解决此问题应该无关紧要,因为核心问题是 System.Delegate(我想让您知道,因为我将链接到一些 Unity 文档以进行澄清)。

我有一个自定义窗口,它具有自定义更新功能DirectorUpdate。无论用户/窗口在做什么,我都需要这个函数来运行每个编辑器更新。

为了在每次编辑器更新时调用它,我将我的方法与 Delegate EditorApplication.update 结合起来:

protected void OnEnable()

    // If I do the below, base EditorApplication.update won't be called anymore.
    // EditorApplication.update = this.DirectorUpdate;
    // So I need to do this:
    EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);

    ... // Other stuff

请注意,这是在窗口的 OnEnable 内完成的。

问题在于 OnEnable 在一次运行期间可能会被多次调用(例如,在单个编辑器会话期间关闭窗口然后重新打开窗口时)导致强>

EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);

被多次调用,意味着我的更新方法(this.DirectorUpdate)每次更新最终都会被多次调用,这会导致一些严重的错误。

所以, 问题是我如何检查EditorApplication.update 是否已经有我的方法“内部”。 (在里面,我当然是说它已经是System.Delegate.Combine(d) 给代表了。)

我知道可能还有其他解决方案,例如在窗口关闭时将 EditorApplication.update 恢复到之前的状态,但这并不适用于所有情况(例如,在窗口刷新期间也会调用 OnEnable 和这样)并且该错误将持续存在。 (另外,如果另一个窗口在此窗口打开时与 EditorApplication.update 连接怎么办?)因此,最好的解决方案是在 Delegate.Combine 之前检查 EditorApplication.update 是否已经调用此方法。

【问题讨论】:

【参考方案1】:

我认为你走的是复杂的道路;)

订阅和取消订阅事件和委托就像使用运算符 +=-= 一样简单

protected void OnEnable()

    // You can substract your callback even though it wasn't added so far
    // This makes sure it is definitely only added once (for this instance)
    EditorApplication.update -= DirectorUpdate;
    
    // This basically internally does such a Combine for you
    EditorApplication.update += DirectorUpdate;
    
    ... // Other stuff


private void OnDisable()

    // Remove the callback once not needed anymore
    EditorApplication.update -= DirectorUpdate;

这样您还可以打开此窗口的多个实例,它们都将分别接收回调。


顺便说一句,如果这实际上是关于 EditorWindow 那么 afaik 你不应该使用 OnEnabled 但你宁愿使用 Awake

在新窗口打开时调用。

OnDestroy

【讨论】:

当,我对这个答案充满希望;但是,我只是对其进行了测试,但它似乎不起作用:关闭并重新打开窗口后,错误仍然存​​在。此外,JetBrains 骑手给了我这个警告:“委托减法有不可预测的结果”。 请原谅我的最后一条评论 - 它似乎确实有效! (我犯了一个小错误)但是,似乎 OnDisable() 部分对于它的工作是绝对必要的(我猜是因为否则旧窗口的“实例”不会被删除 om 委托减法)。我将对此进行进一步测试并报告,可能会给这个答案打勾:) @danglingPointer 完全是 ;) 正如所说,一旦不再需要,总是删除回调。如果窗口关闭但您不删除回调,您将收到 NullReferenceExceptions,因为它仍会尝试从不再存在的对象调用实例化方法【参考方案2】:

我不熟悉System.Delegate.Combine(d) 的作用,但您可以考虑代替启用/禁用您的窗口,每次都销毁和实例化它,并将您的代码移动到StartAwake 以便它每个窗口“激活”只调用一次。

最后但同样重要的是,在 OnDisable 中使用强大的布尔值,以便在组件被禁用时处理组合执行。像这样:

bool omgIWasDisabled;
protected void OnEnable()

    if (!omgIWasDisabled) 
        EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
    
   
    ... // Other stuff
  

void OnDisable() 
    omgIWasDisabled = true;

希望这些都能解决。

【讨论】:

您可以通过将 bool 'omgIWasAdded' 设为静态并在 OnEnable() 中的主代码之后将其设置为 true(而不必处理 OnDisable())来简化此操作,无论如何我想到了这个解决方案,它很可能就是我得到的。

以上是关于Delegate.Combine:如何检查多播(可组合)委托中是不是已经有委托?的主要内容,如果未能解决你的问题,请参考以下文章

MCS多播交换光开关实现网络灵活配置

委托的发展

如何让两个多播套接字侦听具有相同端口的两个多播通道

多播与Hazelcast服务自动发现,如何检测多播是否可用

如何分析多播消息流?

单播、广播和多播IP地址