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)
的作用,但您可以考虑代替启用/禁用您的窗口,每次都销毁和实例化它,并将您的代码移动到Start
或Awake
以便它每个窗口“激活”只调用一次。
最后但同样重要的是,在 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:如何检查多播(可组合)委托中是不是已经有委托?的主要内容,如果未能解决你的问题,请参考以下文章