通过拉姆达前pressions获得局部变量(和参数)在运行时的名称 [英] Getting names of local variables (and parameters) at run-time through lambda expressions

查看:118
本文介绍了通过拉姆达前pressions获得局部变量(和参数)在运行时的名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很感兴趣,在重构安全的方式获取局部变量(和参数)在运行时的名称。我有以下的扩展方法:

 公共静态字符串GetVariableName< T>(出pression< Func键< T>> variableAccessEx pression)
{
    VAR memberEx pression = variableAccessEx pression.Body为MemberEx pression;
    返回memberEx pression.Member.Name;
}
 

...返回通过一个lambda EX pression捕获的变量的名称:

 静态无效的主要(字串[] args)
{
    Console.WriteLine(GetVariableName(()=>参数));
    //输出:ARGS

    INT NUM = 0;
    Console.WriteLine(GetVariableName(()=> NUM));
    //输出:NUM
}
 

不过,这只是工作,因为C#编译器推动的任何局部变量(和参数)被捕获在匿名函数同名的实例变量幕后编译器生成的类中(每<一个href="http://stackoverflow.com/questions/7220626/local-variable-and-ex$p$pssion-trees/7220792#7220792">Jon飞碟双向)。如果不是这种情况下,中投的<一个href="http://msdn.microsoft.com/en-us/library/system.linq.ex$p$pssions.lambdaex$p$pssion.body.aspx"><$c$c>Body到<一个href="http://msdn.microsoft.com/en-us/library/system.linq.ex$p$pssions.memberex$p$pssion.aspx"><$c$c>MemberEx$p$pssion会失败,因为 MemberEx pression 重presents字段或属性的访问。

这是可变的促销记录的行为,或者是一个实现细节主题在其他版本的框架变化​​?

注:这个问题是<一个泛化href="http://stackoverflow.com/questions/10759632/lambda-ex$p$pssions-for-refactor-safe-argumentexception">my前者在参数验证。

解决方案
  

更新:看起来这可能不再是从C#6的问题,这将引入的 nameof 运营商来解决这样的场景

看来,回答我的问题是;特征是非标准化。这种情况似乎更为暗淡比我原先怀疑;不仅是促进捕获变量的非标准化的,但这样的转换匿名函数自己前pression树重新presentations的完整说明。

此的含义是,即使简单的匿名功能,如在下面的,不能保证导致在整个框架的不同实现(直至转换标准化的)一致的前pression树木:

 防爆pression&LT; Func键&LT; INT,INT,INT&GT;&GT;添加=(INT X,int y)对=&GT; X + Y;
 

以下摘录从 C#语言规范4.0 <拍摄/ A>(强调在所有情况下)。

从4.6防爆pression树类型:

  

泛型类型的精确定义防爆pression&LT; D​​&GT; ,以及构建一个前pression树时,precise规则匿名函数转换为一个前pression乔木型,都是本规范的范围之内,和其他地方进行了描述。

从6.5.2评估的匿名函数转换为EX pression树类型:

  

一个匿名函数到前pression树类型的转换产生EX pression树(§4.6)。更多precisely,匿名函数转换的评估导致了重新presents匿名函数本身的结构对象结构的施工。 前pression树的precise结构,以及创建它的确切过程,是实现定义的。

在6.5.3实现示例第三个例子演示了一个匿名函数,捕获一个局部变量的转换,并证实了我的问题中提到的变量推广:

  

局部变量的生存期,现在必须延伸到匿名功能委托的至少寿命。这可以通过吊装局部变量来实现成一个编译器生成的类的一个域。局部变量(§7.15.5.2)的实例化则相当于创建编译器生成的类的实例,和访问本地变量对应的编译器生成的类的实例访问字段。

这是进一步证实在本节的末尾:

  

这里施加到捕获局部变量相同的技术也可以被转换时匿名函数ex的pression树木用于:参照所生成的编译器的对象可以被存储在恩pression树,和访问本地变量可以被重新presented因为现场对这些对象访问。这种方法的优点是,它允许解禁局部变量来代表和EX pression树木之间共享。

不过,有一个免责条款部分的开头:

  

这里描述的实现是基于由Microsoft C#编译器相同的原则,但这绝不是一个强制性的实现方式,也不是唯一一个可能的。它只是简单地提到了转换到前pression树,因为它们的准确语义是本规范的范围之内。

P.S。埃里克利珀<一href="http://stackoverflow.com/questions/8796056/creating-ex$p$pssion-trees-from-lambda#comment10973272_8796306">confirms在此评论的是,EX pression树规范从来没有发货。存在一个防爆pression树木V2规格下的DLR文件上codePLEX,但其范围似乎并没有涵盖匿名函数来恩pression树在C#中的转换。

I’m interested in retrieving the names of local variables (and parameters) at run-time in a refactor-safe manner. I have the following extension method:

public static string GetVariableName<T>(Expression<Func<T>> variableAccessExpression)
{
    var memberExpression = variableAccessExpression.Body as MemberExpression;
    return memberExpression.Member.Name;
}

…which returns the name of the variable captured through a lambda expression:

static void Main(string[] args)
{
    Console.WriteLine(GetVariableName(() => args));
    // Output: "args"

    int num = 0;
    Console.WriteLine(GetVariableName(() => num));
    // Output: "num"
}

However, this only works because the C# compiler promotes any local variables (and parameters) that are captured in anonymous functions to instance variables of the same name within a compiler-generated class behind the scenes (per Jon Skeet). If this were not the case, the cast of Body to MemberExpression would fail, since MemberExpression represents field or property access.

Is this variable promotion documented behaviour, or is it an implementation detail subject to change in other versions of the framework?

Note: This question is a generalization of my former one on argument validation.

解决方案

Update: It seems like this might no longer be an issue from C# 6, which will introduce the nameof operator to address such scenarios.

It appears that the answer to my question is no; the feature is non-standardized. The situation seems even bleaker than I’d originally suspected; not only is the promotion of captured variables non-standardized, but so is the entire specification of converting anonymous functions to their expression tree representations.

The implication of this is that even straightforward anonymous functions, such as the below, are not guaranteed to result in consistent expression trees across different implementations of the framework (until the conversion is standardized):

Expression<Func<int, int, int>> add = (int x, int y) => x + y;

The following excerpts are taken from the C# Language Specification 4.0 (emphasis added in all cases).

From "4.6 Expression tree types":

The exact definition of the generic type Expression<D> as well as the precise rules for constructing an expression tree when an anonymous function is converted to an expression tree type, are both outside the scope of this specification, and are described elsewhere.

From "6.5.2 Evaluation of anonymous function conversions to expression tree types":

Conversion of an anonymous function to an expression tree type produces an expression tree (§4.6). More precisely, evaluation of the anonymous function conversion leads to the construction of an object structure that represents the structure of the anonymous function itself. The precise structure of the expression tree, as well as the exact process for creating it, are implementation defined.

The third example in "6.5.3 Implementation example" demonstrates the conversion of an anonymous function that captures a local variable, and confirms the variable promotion mentioned in my question:

The lifetime of the local variable must now be extended to at least the lifetime of the anonymous function delegate. This can be achieved by "hoisting" the local variable into a field of a compiler generated class. Instantiation of the local variable (§7.15.5.2) then corresponds to creating an instance of the compiler generated class, and accessing the local variable corresponds to accessing a field in the instance of the compiler generated class.

This is further corroborated at the end of the section:

The same technique applied here to capture local variables can also be used when converting anonymous functions to expression trees: References to the compiler generated objects can be stored in the expression tree, and access to the local variables can be represented as field accesses on these objects. The advantage of this approach is that it allows the "lifted" local variables to be shared between delegates and expression trees.

However, there is a disclaimer at the beginning of the section:

The implementation described here is based on the same principles used by the Microsoft C# compiler, but it is by no means a mandated implementation, nor is it the only one possible. It only briefly mentions conversions to expression trees, as their exact semantics are outside the scope of this specification.

P.S. Eric Lippert confirms in this comment that the expression tree specs were never shipped. There exists an Expression Trees v2 Spec under the DLR documentation on CodePlex, but its scope does not appear to cover the conversion of anonymous functions to expression trees in C#.

这篇关于通过拉姆达前pressions获得局部变量(和参数)在运行时的名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆