Getting closure variable’s data from lambda expression in C#
Hi everyone! Today I want to talk about closures and extracting their values from lamda expression.
The problem
One of my project has a lot of Action
and Func
delegates as parameters for methods. It has them everyevere and I got a task to write some unit-tests for some of these methods.
Let’s imagine that there is a method that take Action
like a parameter.
public void Execute(Action action)
{
//... Some code
action();
//... Some code
}
The delegate can be initialised with lambda expression.
var thisIsClosure = "ThisIsClosure";
Action actionWithClosure = () =>
{
Сonsole.WriteLine(thisIsClosure);
};
So, we call them in a way like in a snippet below.
Execute(actionWithClosure);
What I did
Because I was writing unit-tests I had to get some values from Action
for Asserts
.
But It tended to be a real problem, because I couldn’t use Expression
, because code smells when you change it for testing reasons.
Please Note: If you change your code, due to some unit-tests reasons, it’s the awful practice. Please, don’t do this.
So, I decided to write a simple parser with using of Reflection
.
public static class LambdaVariableExtractor
{
public static TValue ExtractValue<TValue>(
this Delegate lambda,
string variableName)
where TValue : class
{
if (lambda == null)
throw new NullReferenceException("Empty lambda");
var field = lambda.Method.DeclaringType?.GetFields
(
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.Static
)
.Single(x => x.Name == name);
if (field == null)
throw new NullReferenceException("There isn't a variable with this name");
return field.GetValue(lambda.Target) as TValue;
}
}
In this code, first of all we check lambda expression for exisiting and then do some magic.
What is a DeclaringType
in this context? For uderstanding this, we should take a short look on some parts of C# specification.
As we all know, lambda expressions are just sugar.
7.15 Anonymous function expressions An anonymous function is an expression that represents an “in-line” method definition. An anonymous function does not have a value or type in and of itself, but is convertible to a compatible delegate or expression tree type. The evaluation of an anonymous function conversion depends on the target type of the conversion: If it is a delegate type, the conversion evaluates to a delegate value referencing the method which the anonymous function defines. specification
In this context lambda is some kind of anonymous function. Let’s take a look, how compilator doing some job for us.
There is a class named the strange name <>c__DisplayClass0_0
in the picture. And this is a class which was generated by compilator.
Usually this class contains only your lambda as a method. But sometimes, when you have closures it starts to contain some variable.
In example, you can see that there is thisIsClosure
variable there.
So, DeclaringType
returns Type
for this generated class. After that it’s easy to fetch fields of this class.
And the last thing, If you want to get values from these fields you should pass Target
as the parameter to the GetValue
function.
Target
keeps reference to the object of the generated class and we need to pass it for right working of Reflection
.
You can find this example’s code on github.
Conclusion
That’s all. I’ll be happy, If this article helps you.
Leave a Comment