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:
- Call by value (struct) - the current value of
this.Value
gets passed, so if them_TaskQueue
executes it 5 minutes later, the value will not be in its recent state, it will be whatever it was when first referencing. - Call by reference (reference type) - then the most recent state of
Value
will be referenced during execution of action but if I changethis.Value
to another reference before execution of action, it will still be pointing to the old reference - Call by name (both) - where
this.Value
will be evaluated when the action gets called. I believe the actual implementation would be holding a reference tothis
then evaluateValue
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
.
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:
If it is a mutable reference type you could make a local clone / deep copy.
The real key is to remember that scope is lexical; it's something the compiler takes care of. So it captures variables, not their values. Whether those values are value types or reference types is another matter completely.
Maybe a slightly more extreme example of altering the behavior of the delegate will help:
prints "something else entirely". In that light, it doesn't really matter how many times you wrap, save, or move the function; it still refers to the variable it enclosed. So, in short, what matters is the value of the enclosed variable when the delegate's actually executed.
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
The compiler generates code as though you had actually written:
Does that answer your question?