Several methods in our code base use a 'MaybeObject' that can be passed into functions when a result might be known, or might rely on an external webservice call that has not yet been carried out. For example, the property below can either have a specified known value, or if not specified and called after the async call is complete it will return the result of the Async call.
private string _internalString;
public string stringProp
{
get
{
if (!string.IsNullOrEmpty(_internalString))
return _internalString;
return resultOfAsyncCallFromSomewhereElse;
}
set { _internalString = value; }
}
Obviously, trying to reference the property before the async call is complete would cause a null reference exception so we also have a flag to check if the value is available.
The question is, in the code below would the creation of the lambda try and evaluate stringProp
(which might not be populated yet), or would evaluation be deferred until the resulting Action is called (which would be after checking the async operation is complete)?
public Action ExampleMethod(MaybeObject maybe)
{
return () => doSomethingWithString(maybe.stringProp);
}
In C# all would be evaluated whilst execution since C# lambda Closures is not a true Closure which able to resolve a state of captured environment in moment of creation.
() => doSomethingWithString(maybe.stringProp);
Here is even reference maybe
could be null and you won't got any issues like NullReferenceException until executing a delegate. This trick sometimes used for late-bound value resolving.
Wikipedia: Closure:
A closure retains a reference to the environment at the time it was created (for example, to the current value of a local variable in the enclosing scope) while a generic anonymous function need not do this.
Nice overview of C# Closure specifics - Anonymous methods and closures in C#
From the definition of Closure it can be inferred that Closure remembers the values of the variables during its creation. However in C# the outer local variable (i in this case) is shared with the anonymous method by creating it on the heap. This means any change to it in the anonymous method changes the original value and when the method is called the second time it gets the modified value of i as 1 (see second line of output). This leads many to argue that anonymous method is not actually a Closure as by its definition the value of a variable at the time of creation of the Closure should be remembered and not modifiable.
Evaluation will be deferred until the resulting Action is called.
The code referenced by a delegate is only executed when the delegate itself is explicitly invoked, regardless of how the delegate itself was created.
For example, these ways of passing the code to execute through the Action delegate are all equivalent, and the doSomethingWithString
method won't be executed until a call to Action()
is made:
Explicit method (.NET 1.1):
private MaybeObject maybe;
public Action ExampleMethod()
{
return new Action(DoSomethingWithMaybeObject);
}
private void DoSomethingWithMaybeObject()
{
doSomethingWithString(maybe.stringProp)
}
Anonymous method (.NET 2.0):
public Action ExampleMethod(MaybeObject maybe)
{
return delegate() { doSomethingWithString(maybe.stringProp) };
}
Lambda (.NET 3.5):
public Action ExampleMethod(MaybeObject maybe)
{
return () => doSomethingWithString(maybe.stringProp);
}
See also:
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