c# - 用于重构安全 ArgumentException 的 Lambda 表达式

标签 c# .net .net-4.0 lambda

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.

我喜欢使用 lambda 表达式创建 INotifyPropertyChanged 接口(interface)的重构安全实现的想法,使用类似于 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;

A MemberExpression代表“访问字段或属性”。它保证在 INotifyPropertyChanged 情况下正常工作,因为 lambda 表达式将是属性访问。

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

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

最佳答案

闭包:如您所述,对于参数访问,C# 编译器(是的,特别是编译器)创建一个闭包类,其中包含实例字段以存储捕获的参数变量的值。这会随着 C# 编译器的 future 版本而改变吗?当然。也许在未来的 C# 版本中,生成的闭包类将具有随机命名的变量,因为名称在运行时并不重要。此外,您拥有的代码可能不适用于其他 .NET 语言。您会注意到 VB .NET 生成的表达式树和闭包类有时与 C# 略有不同...

我不确定您当前的实现是否也适用于结构(虽然我可能记错了......我正在考虑处理装箱的情况可能只适用于 Expression<Func<T, object>>(阅读,请尝试你自己)。

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

至于性能:请在这里非常小心。你已经说过传递两个参数会更有效,这样你就不需要编译和评估 lambda ....但要清楚一点,你说的是每次编译时都会有 15 到 30 毫秒的命中时间,并且评估。

关于c# - 用于重构安全 ArgumentException 的 Lambda 表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10759632/

相关文章:

c# - 我可以在同一个解决方案中有一个使用 .NET Framework 4.5.2 的项目和另一个使用 4.6.2 的项目吗?

c# - 如何防止列表中的项目在未填写且应为 "0"时返回 "null"?

visual-studio-2010 - 适用于 .NET 4.0 的 FxCop

c# - 在 C# 中如何使用 ExecuteNonQuery 从存储过程中获取返回值

C# 反射——加载外部 DLL 和所有依赖项

.net - 有没有办法从表中获取不同的 PartionKeys

c# - 从 C# 中的 .resx 文件读取字符串

c# - 是否可以创建具有数千个长时间运行的 TCP 连接的可伸缩 WCF 服务?

c# - 我们可以为两个事件设置相同的委托(delegate)吗

c# - 连接打开时 Firebase SSE 发送整个 json 文件