Search code examples
c#unity-game-enginedelegatesconcatenationmulticastdelegate

Delegate.Combine: How to Check if multicast (combinable) delegates already has a delegate inside of it?


I am using Unity 3D, however, that information should be irrelevant for solving this problem as the core problem is System.Delegate (I wanted to let you know as I'll be linking to some Unity docs for clarification).

I have a custom window that has a custom update function DirectorUpdate. I need this function to run every editor update regardless of what the user/window is doing.

In order for this to be called every editor update, I Combine my method with the 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
}

Note that this is done inside a window's OnEnable.

The problem is that OnEnable can be called more than once during a single run (for example, when closing the window and then reopening the window during a single editor session) causing

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

to be called multiple times, meaning my update method (this.DirectorUpdate) will eventually get called multiple times per update, which is causing some serious bugs.

So, the question is how do I check if EditorApplication.update already has my method "inside" of it. (By inside of it, I of course mean it has already been System.Delegate.Combine(d) to the delegate.)

I am aware that there could be other solutions, for example restoring EditorApplication.update to what it was prior when the window closes, however that won't account for all situations (for example, OnEnable also gets called during window refresh and such) and the bug will persist. (Also, what if another window Concatenates with EditorApplication.update while this window is open?) As such, the best solution would be to check if EditorApplication.update is already callin this method BEFORE Delegate.Combine-ing it.


Solution

  • I think you took the complicated road ;)

    Subscribing and unsubscribing events and delegates is as simple as using the operators += and -= like

    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;
    }
    

    This way you can also have multiple instances of this window open and they all will receive the callback individually.


    Btw if this is actually about an EditorWindow then afaik you should not use OnEnabled but you would rather use Awake

    Called as the new window is opened.

    and OnDestroy.