Working with event Action, unsubscribing from the event

public event Action<GameObject> SelectCallbackMethods;

void Start()
{
    SelectCallbackMethods += (obj) => CharacterController.Instance.SelectObj(obj.gameObject);
}

void OnMouseUp()
{
    SelectCallbackMethods.Invoke(this.gameObject);
    SelectCallbackMethods -= (obj) => CharacterController.Instance.SelectObj(obj.gameObject);
}

How can I unsubscribe a method in this situation? Unsubscribing using ( - = ) after Invoke does not work, the method is still called.

Is this the right approach to using events? Found a solution: creating a variable for each Action, but does it make sense? Wouldn't it be the same if I just called the method at the right moment from this class:

Action<GameObject> handler;

void Start()
{
    handler = (obj) => CharacterController.Instance.SelectObj(obj.gameObject);
    SelectCallbackMethods += handler;
}

void OnMouseUp()
{
    SelectCallbackMethods.Invoke(this.gameObject);
    SelectCallbackMethods -= handler;
}
Author: A K, 2019-07-21

1 answers

The reason for this behavior is simple: the compiler expands each anonymous method into a separate method.

I.e. from

using System;
public class Program
{
    public event Action<object> MyEvent;

    void Start()
    {
        MyEvent += obj => Console.WriteLine(obj);
    }

    void OnMouseUp()
    {
        MyEvent.Invoke(this);
        MyEvent -= obj => Console.WriteLine(obj);
    }
}

You will get something like the following

using System;

public class Program
{
    private sealed class auxiliaryClass
    {
        public static readonly auxiliaryClass instance = new auxiliaryClass();

        public static Action<object> method1;

        public static Action<object> method2;

        internal void method1_Start(object obj)
        {
            Console.WriteLine(obj);
        }

        internal void method2_OnMouseUp(object obj)
        {
            Console.WriteLine(obj);
        }
    }

    public event Action<object> MyEvent;

    private void Start()
    {
        MyEvent += method1 ?? (method1 = auxiliaryClass.method1_Start);
    }

    private void OnMouseUp()
    {
        this.m_MyEvent(this);
        MyEvent -= method2 ?? (method2 = auxiliaryClass.method2_OnMouseUp);
    }
}

I omitted the irrelevant details in this question, the verbatim code can be seen here

Accordingly, when unsubscribing from an event, method2 will not be found in it and it will simply be ignored, method1 will remain among the subscribers.

To solve this problem, you need to explicitly move the handler to a separate method:

using System;
public class Program
{
    public event Action<object> MyEvent;

    void Start()
    {
        MyEvent += MyAction;
    }

    void OnMouseUp()
    {
        MyEvent.Invoke(this);
        MyEvent -= MyAction;
    }

    void MyAction(object obj) => Console.WriteLine(obj);
}

demonstration

Well, or rewrite the code without using the event at all, for example, with a simple bool flag:

using System;
public class Program
{
    public event Action<object> MyEvent;

    bool handled;

    void Start()
    {
        handled = false;
    }

    void OnMouseUp()
    {
        if (handled) return;
        handled = true;

        MyAction(this);
    }

    void MyAction(object obj) => Console.WriteLine(obj);
}
 1
Author: Андрей NOP, 2019-07-21 10:28:52