LAMBDA EX pressions的重构安全的ArgumentException重构、安全、EX、LAMBDA

2023-09-04 09:59:13 作者:姐的高傲ジ你不懂♀

注意:请参照Getting局部变量(和参数)在运行时,通过拉姆达EX pressions名的一概括这个问题,以及一些答案。

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 EX pressions创建 INotifyPropertyChanged的界面重构安全实现的想法,用code相似,所埃里克·德Carufel 。

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.

我尝试用实施类似的东西提供的参数名称的的ArgumentException (或其派生类)的重构安全的方式。

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;

一个 MemberEx pression 重新presents访问字段或属性。这是保证在 INotifyPropertyChanged的情况下正常工作,因为拉姆达EX pression将是一个属性访问。

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.

不过,在我的code以上,拉姆达EX pression是一个语义的参数的访问,而不是一个字段或属性的访问。在code工作的唯一原因是,C#编译器推动的任何局部变量(和参数)被捕获在匿名函数实例变量幕后编译器生成的类中。这是由乔恩飞碟双向的佐证。

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.

我的问题是:是.NET规范中记录(促进捕获参数实例变量的)这种行为,或者是它只是实现的细节,可能在替代的实现或框架的未来版本中改变?具体而言,可能会有环境下 parameterAccessEx pression.Body是MemberEx pression 返回

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?

推荐答案

闭包:正如你所说,对于参数访问,C#编译器(是的,特别是编译器)创建了一个包含实例字段存储的值封闭类捕获的参数变量。莫非这一变化与C#编译器的未来版本?当然。也许在C#的未来版本中,所产生的封闭班都会有随机命名的变量,因为这个名字并不重要,在运行时。此外,code,你有可能不适用于其他.NET语言工作。你会发现,VB .NET生成EX pression树木和封闭班略有不同从C#有时...

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

我不知道,如果你当前的实施将为结构无论是工作(尽管我可能是错误记忆......我想对付拳击的情况可能只适用于防爆pression&LT; Func键&LT; T,对象&gt;&GT; (阅读,请尝试youself)

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#的未来版本中改变?可能不会。如果是这样,你可以改变你的内部实现以可能处理它...

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

至于性能:请非常小心这里。你已经表示,将是更有效地传递两个参数,这样你就不会每次编译时需要编译和评估拉姆达....但仅仅是明确的,你在谈论一个15至30ms命中这里评估。

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.