I have this function that i call multiple times so i need to wipe the assigned actions and make sure they don't duplicate:
My OnClick looks like this:
public class Input {
public Action<Vector3> OnClick;
}
Then in a different class I have:
// _input is a refence to class Input
public void Init()
{
_input.OnClick -= OnLeftClick;
_input.OnClick = OnLeftClick;
var list = _input.OnClick.GetInvocationList();
var list = _input.OnClick.GetInvocationList();
Debug.Log("DEBUG: " list.Length);
foreach (var item in list)
Debug.Log(item.Method.ToString());
}
After calling this function a few times in a row the output gives:
DEBUG: 1
Void OnLeftClick(Vector3)
DEBUG: 2
Void OnLeftClick(Vector3)
Void OnLeftClick(Vector3)
DEBUG: 5
Void OnLeftClick(Vector3)
Void OnLeftClick(Vector3)
Void OnLeftClick(Vector3)
Void OnLeftClick(Vector3)
Void OnLeftClick(Vector3)
So my method OnLeftClick is getting called multiple times, but I don't know why. How do you clear the method from the action so I can then re-assign it to prevent duplicates?
CodePudding user response:
When public Action<Vector3> OnClick;
is not marked event
, i.e. it is a pure field, everyone has access to it in its entirety (not just through add
and remove
accessors). So they can clear it with _input.OnClick = null;
.
However, are you sure you do not want an event?
For delegates, the convention is that a null
reference is used (rather than some empty object) when there are no functions.
If instead you decide for an auto-event (so-called field-like event), access to the automatic (implicit) backing field is only from inside the class itself. Of course you can create a method in the class that clears the event, and make the method nonprivate, but maybe it is not the best design.
CodePudding user response:
The problem here is that your OnClick
event subscribers are different from one Init
call to another. Probably you are invoking the Init
method on different objects or reference.
I hope this example clarify the situation:
class A {
public Action<Vector3> OnClick;
// using method group
public A() : this(_OnClick) { }
// using anonymous method/action reference
public A(Action<Vector3> a) => OnClick = a;
...
public void Init() {
_init.OnClick -= OnClick;
_init.OnClick = OnClick;
}
public void _OnClick(Vector3 v) { }
}
Problem with group method
A a1 = new A();
a1.Init();
// I will display the subscriber list like: _init.OnClick -> []
// Here you are applying:
// _init.OnClick -= a1.OnClick; -> _init.OnClick [ ]
// _init.OnClick = a1.OnClick; -> _init.OnClick [ a1.OnClick ]
// the subscriber is a1
A a2 = new A();
// But here you are applying:
// _init.OnClick -= a2.OnClick; -> _init.OnClick [ a1.OnClick ]
// _init.OnClick = a2.OnClick; -> _init.OnClick [ a1.OnClick, a2.OnClick ]
// the subscriber is a2
a2.Init();
Problem with anonymous methods
A a = new A(v => Console.WriteLine($"A1: {v}"));
a.Init();
// Here you are applying:
// _init.OnClick -= a.OnClick; -> _init.OnClick [ ]
// _init.OnClick = a.OnClick; -> _init.OnClick [ (v => Console.WriteLine($"A1: {v}")) ]
// the subscriber is (v => Console.WriteLine($"A1: {v}"))
a.OnClick = v => Console.WriteLine($"A2: {v}");
a.Init();
// But here you are applying:
// _init.OnClick -= a.OnClick; -> _init.OnClick [ (v => Console.WriteLine($"A1: {v}")) ]
// and a.OnClick is (v => Console.WriteLine($"A2: {v}"))
// _init.OnClick = a.OnClick; -> _init.OnClick [ (v => Console.WriteLine($"A1: {v}")), (v => Console.WriteLine($"A2: {v}")) ]
// the subscriber is (v => Console.WriteLine($"A2: {v}"))
a2.Init();
NOTE: it also occurs with the same "lambda body" but different anonymous method:
Action<Vector3> m1 = v => Console.WriteLine($"A: {v}");
Action<Vector3> m2 = v => Console.WriteLine($"A: {v}");
// m1 differs from m2 because are compiled to two different
// anonymous methods, even if they have the same code
a.OnClick = m1;
a.Init();
// Here you are applying:
// _init.OnClick -= a.OnClick; -> _init.OnClick [ ]
// _init.OnClick = a.OnClick; -> _init.OnClick [ m1 ]
// the subscriber is m1
a.OnClick = m2;
// But here you are applying:
// _init.OnClick -= a.OnClick; -> _init.OnClick [ m1 ]
// _init.OnClick = a.OnClick; -> _init.OnClick [ m1, m2 ]
// the subscriber is m2 (!= m1)
a.Init();
Same applies if the _init
changes from one instance of A
to another (eg. is not a static member)
Take also a look to Unsubscribe - MDN.