Update: This is no longer an issue from C# 6, which has introduced the
nameof
operator to address such scenarios (see MSDN).Note: Refer to “Getting names of local variables (and parameters) at run-time through lambda expressions” for a generalization of this question, as well as some answers.
I like the idea of using lambda expressions to create refactor-safe implementations of the INotifyPropertyChanged
interface, using code similar to that provided by Eric De Carufel.
I’m experimenting with implementing something similar for providing the parameter name to an ArgumentException
(or its derived classes) in a refactor-safe manner.
I have defined the following utility method for performing null
checks:
public static void CheckNotNull<T>(Expression<Func<T>> parameterAccessExpression)
{
Func<T> parameterAccess = parameterAccessExpression.Compile();
T parameterValue = parameterAccess();
CheckNotNull(parameterValue, parameterAccessExpression);
}
public static void CheckNotNull<T>(T parameterValue,
Expression<Func<T>> parameterAccessExpression)
{
if (parameterValue == null)
{
Expression bodyExpression = parameterAccessExpression.Body;
MemberExpression memberExpression = bodyExpression as MemberExpression;
string parameterName = memberExpression.Member.Name;
throw new ArgumentNullException(parameterName);
}
}
Argument validation may then be performed in a refactor-safe manner using the following syntax:
CheckNotNull(() => arg); // most concise
CheckNotNull(arg, () => args); // equivalent, but more efficient
My concern lies in the following lines:
Expression bodyExpression = parameterAccessExpression.Body;
MemberExpression memberExpression = bodyExpression as MemberExpression;
A MemberExpression
represents “accessing a field or property”. It is guaranteed to work correctly in the INotifyPropertyChanged
case, since the lambda expression would be a property access.
However, in my code above, the lambda expression is semantically a parameter access, not a field or property access. The only reason the code works is that the C# compiler promotes any local variables (and parameters) that are captured in anonymous functions to instance variables within a compiler-generated class behind the scenes. This is corroborated by Jon Skeet.
My question is: Is this behaviour (of promoting captured parameters to instance variables) documented within the .NET specification, or is it just an implementation detail that may change in alternate implementations or future releases of the framework? Specifically, may there be environments where parameterAccessExpression.Body is MemberExpression
returns false
?
Closures: As you stated, for parameter access, the C# compiler (yes, specifically the compiler) creates a closure class that contains instance fields to store the value of your captured parameter variable. Could this change with future versions of the C# compiler? Sure. Maybe in a future version of C#, the generated closure classes will have randomly named variables since the name doesn't really matter at runtime. Further, the code you have might not work for other .NET languages. You'll notice that VB .NET generates expression trees and closure classes slightly differently from C# sometimes...
I am not sure if your current implementation will work for structs either (though I could be mis-remembering...the situation I'm thinking of dealing with boxing might only apply for
Expression<Func<T, object>>
(read, please try it youself).Anyway...all this said...will it change in future versions of C#? Probably not. If it does, you could change your internal implementation to handle it probably...
As for performance: please be really careful here. You already said it would be more efficient to pass two arguments so you don't need to compile and evaluate the lambda....but just to be clear, you're talking about a 15 to 30ms hit here each time you compile and evaluate.