6

更新:这不再是 C# 6 中的问题,它引入了nameof操作符来解决此类场景(请参阅MSDN)。

注意:参考“<a href="https://stackoverflow.com/questions/11063502/getting-names-of-local-variables-and-parameters-at-run-time-through-lambda-exp">获取在运行时通过 lambda 表达式命名局部变量(和参数)”以概括这个问题,以及一些答案。

我喜欢使用 lambda 表达式创建INotifyPropertyChanged接口的重构安全实现的想法,使用类似于Eric De Carufel提供的代码。

我正在尝试实现类似的东西ArgumentException,以重构安全的方式向(或其派生类)提供参数名称。

我已经定义了以下用于执行null检查的实用方法:

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);
    }
}

然后可以使用以下语法以重构安全的方式执行参数验证:

CheckNotNull(() => arg);           // most concise
CheckNotNull(arg, () => args);     // equivalent, but more efficient

我的担忧在于以下几行:

Expression bodyExpression = parameterAccessExpression.Body;
MemberExpression memberExpression = bodyExpression as MemberExpression;

AMemberExpression表示“访问字段或属性”。它保证在这种INotifyPropertyChanged情况下正常工作,因为 lambda 表达式将是一个属性访问。

但是,在我上面的代码中,lambda 表达式在语义上是参数访问,而不是字段或属性访问。代码工作的唯一原因是 C# 编译器将匿名函数中捕获的任何局部变量(和参数)提升为后台编译器生成的类中的实例变量。Jon Skeet证实了这一点。

我的问题是:这种行为(将捕获的参数提升为实例变量)是否记录在 .NET 规范中,或者它只是一个可能在替代实现或框架的未来版本中发生变化的实现细节?具体来说,可能有parameterAccessExpression.Body is MemberExpression返回的环境false吗?

4

1 回答 1

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).

无论如何......所有这一切......它会在未来的 C# 版本中改变吗?可能不是。如果是这样,你可以改变你的内部实现来处理它可能......

至于性能:请在这里非常小心。您已经说过传递两个参数会更有效,因此您不需要编译和评估 lambda ......但为了清楚起见,您每次编译时都会在这里点击 15 到 30 毫秒评估。

于 2012-06-17T19:12:57.697 回答