Let's say we have this class:
// Provides deferred behaviour
public class Command<TResult>
{
private Func<object[], TResult> _executeFunction;
private object[] _args;
public Command(Func<object[], TResult> execution, params object[] arguments)
{
_executeFunction = execution;
_args = arguments;
}
public TResult Execute()
{
return _executeFunction(_args);
}
}
What's the difference between these two anonymous functions?
int a = 1;
int b = 4;
// a and b are passed in as arguments to the function
Command<int> sum = new Command<int>(args => (int)args[0] + (int)args[1], a, b);
// a and b are captured by the function
Command<int> sum2 = new Command<int>(_ => a + b);
Console.WriteLine(sum.Execute()); //Prints out 5
Console.WriteLine(sum2.Execute()); //Prints out 5
I'm specifically looking for performance differences.
Also, we know that if some class is holding a reference of sum2
, then a
and b
will live beyond the scope they were defined on, probably never getting collected by the GC if the function is still referenced anywhere.
Does the same happen with sum
? (Considering the arguments are reference types and not value types as in this example)
When you pass in the variables a
and b
, you are doing just that. The value of 1
and 4
are passed in respectively. However, when you refer to a
and b
within the context (or scope) of the lambda expression the values are "captured". The variables a
and b
, within the scope of the lambda expression are treated as references to the originals outside of the scope, meaning that if they are changed within the scope of the lambda -- so too are the originals. When compiled into IL, they reside in a class where the instances are shared.
static void Main()
{
int a = 1;
int b = 4;
// a and b are passed in as arguments to the function
var sum = new Command<int>(args => (int)args[0] + (int)args[1], a, b);
// a and b are captured by the function
var sum2 = new Command<int>(_ =>
{
var c = a + b;
a++;
b++;
return c;
});
Console.WriteLine(sum.Execute()); //Prints out 5
Console.WriteLine(sum2.Execute()); //Prints out 5
Console.WriteLine("a = " + a); // Prints 2
Console.WriteLine("b = " + b); // Prints 5
Console.ReadLine();
}
There is really small difference in the IL, and I do not believe there is any performance implications worth avoiding one or the other. I would usually prefer the usage of lambda expressions for their readability.
A look at some of the IL generated from some C# lambdas.
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