6

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?

Community
  • 1
  • 1
Douglas
  • 53,759
  • 13
  • 140
  • 188
  • 1
    What about `CheckNotNull(() => ((object)5) as string);` ? – leppie May 25 '12 at 19:13
  • 1
    Personally I decided that whatever `Contract.Requires` does is good enough for me. If a precondition fails, it's a bug. No need to fret about the details. – CodesInChaos May 25 '12 at 19:15
  • @CodeInChaos: What about libraries that will be made available for consumption to third parties? It’s expected that public methods would perform argument-checking and throw `ArgumentException` with the correct parameter name. – Douglas May 25 '12 at 19:32
  • @leppie: My question concerns *argument* validation. The goal is to get the parameter name, not evaluate arbitrary expressions. – Douglas May 25 '12 at 19:54
  • Alternative here: http://stackoverflow.com/questions/869610/resolving-a-parameter-name-at-runtime/14672028#14672028, again not documented. But faster. – nawfal Apr 17 '13 at 13:26

1 Answers1

0

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.

Jeff
  • 35,755
  • 15
  • 108
  • 220
  • I’m accepting this answer since it raises a number of valid points (including the possibility of promoted variables getting renamed, and incompatibility with other .NET languages). For reference, I have also documented my research on the [lack of standardization of this behaviour](http://stackoverflow.com/a/11071271/1149773) under my other question. – Douglas Jun 23 '12 at 12:48