I'm passing a System.Action
as a parameter to a method which does some lengthy operation and want to add more stuff to the invocation list after the Action
has been passed:
class Class1
{
private System.Action onDoneCallback;
void StartAsyncOperation(System.Action onDoneCallback)
{
this.onDoneCallback = onDoneCallback;
// do lengthy stuff
}
void MuchLater()
{
this.onDoneCallBack?.Invoke();
}
}
class Class2
{
public System.Action action;
void DoStuff()
{
action += () => print ("a");
new Class1().StartAsyncOperation(action);
}
{
// ... much later in another place but still before StartAsyncOperation ends
action += () => print ("b");
}
}
However, only the the stuff that was added with +=
before passing the Action as parameter is invoked. So, in this example, it will only print "a"
but not "b"
.
This makes me think that System.Action
is copied when it's passed as parameter (like a primitive type e.g. int
would). So, when +=
is done later, it has no effect on the local copy of action
inside SomeAsyncOperation
.
I thought of passing System.Action
with ref
. However, I need to store it as member variable inside Class1
, and I can't make a member variable a ref
!
So, basically, the question is: how do you add more stuff to the invocation list of a callback after that callback has been passed and the lengthy operation is long on its way but hasn't ended yet.
EDIT:
Ended up replacing
new Class1().StartAsyncOperation(action);
with
new Class1().StartAsyncOperation(() => action?.Invoke());
A delegate type is an immutable reference type, like a string:
s += "\n";
s
is now a reference to a different object. If you pass it to a method, the method gets a reference to this object, not to whatever object s
may refer to next. This lambda returns, and will continue to return, whatever object s
refers to when the lambda is called:
() => s;
The same applies with a += () => {};
: a
is referencing a different object afterwards, but you can create a lambda which executes the current value of a
, whatever that may be.
Hence:
new Class1().StartAsyncOperation(() => action());
Whatever you to do action
after that point, the lambda you passed in has a reference to the current value of action
.
Try it at home:
Action a = () => Console.Write("a");
// This would print "a" when we call b() at the end
//Action b = a;
// This prints "a+" when we call b() at the end.
Action b = () => a();
a += () => Console.Write("+");
b();
One option:
class Class2
{
public List<System.Action> callbacks = new List<System.Action>();
void DoStuff()
{
callbacks.Add(() => print("a"));
new Class1().StartAsyncOperation(() => {
forach(var a in callbacks()
{
a();
}
});
}
{
// ... much later in another place but still before StartAsyncOperation ends
callbacks.Add(() => print ("b"));
}
}
You'll get a closure over the list, so any changes will still be available when the callbacks run.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With