I have a simple question about .net delegates. Say I have something like this:
public void Invoke(Action<T> action)
{
Invoke(() => action(this.Value));
}
public void Invoke(Action action)
{
m_TaskQueue.Enqueue(action);
}
The first function encloses a reference to this.Value
. During runtime, when the first, method with generic parameter gets called, it will provide this.Value
somehow to the second one, but how? These came into my mind:
this.Value
gets passed, so if the m_TaskQueue
executes it 5 minutes later, the value will not be in its recent state, it will be whatever it was when first referencing. Value
will be referenced during execution of action but if I change this.Value
to another reference before execution of action, it will still be pointing to the old referencethis.Value
will be evaluated when the action gets called. I believe the actual implementation would be holding a reference to this
then evaluate Value
on that during actual execution of delegate since there is no call by name.I assume it would be Call of name style but could not find any documentation so wondering if it is a well-defined behavior. This class is something like an Actor in Scala or Erlang so I need it to be thread safe. I do not want Invoke
function to dereference Value
immediately, that will be done in a safe thread for this
object by m_TaskQueue
.
Let me answer your question by describing what code we actually generate for this. I'll rename your confusingly-named other Invoke method; it's not necessary to understanding what's going on here.
Suppose you said
class C<T>
{
public T Value;
public void Invoke(Action<T> action)
{
Frob(() => action(this.Value));
}
public void Frob(Action action)
{ // whatever
}
}
The compiler generates code as though you had actually written:
class C<T>
{
public T Value;
private class CLOSURE
{
public Action<T> ACTION;
public C<T> THIS;
public void METHOD()
{
this.ACTION(this.THIS.Value);
}
}
public void Invoke(Action<T> action)
{
CLOSURE closure = new CLOSURE();
closure.THIS = this;
closure.ACTION = action;
Frob(new Action(closure.METHOD));
}
public void Frob(Action action)
{ // whatever
}
}
Does that answer your question?
The delegate stores a reference to the variable, not the value of it. If you want to keep the current value then (assuming it is a value type) you need to make a local copy of it:
public void Invoke(Action<T> action)
{
var localValue = this.Value;
Invoke(() => action(localValue));
}
If it is a mutable reference type you could make a local clone / deep copy.
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