通过lambda表达式在运行时获取局部变量(和参数)的名称

时间:2012-06-16 12:38:25

标签: c# .net lambda

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

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

...返回通过lambda表达式捕获的变量的名称:

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

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

但是,这只能起作用,因为C#编译器将在匿名函数中捕获的任何局部变量(和参数)提升为幕后编译器生成的类中的同名实例变量(per Jon Skeet) 。如果情况并非如此,那么BodyMemberExpression的广告会失败,因为MemberExpression代表字段或媒体资源访问。

此变量促销是否记录了行为,或者是否是其他版本的框架中可能发生更改的实现细节?

注意:此问题是对my former one on argument validation

的概括

3 个答案:

答案 0 :(得分:13)

  

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

我的问题的答案似乎是没有;该功能是非标准化的。这种情况似乎比我原先怀疑的还要糟糕;不仅是捕获变量的推广是非标准化的,而且将匿名函数转换为表达式树表示的整个规范也是如此。

这意味着即使是直接的匿名函数(如下所示)也不能保证在框架的不同实现中产生一致的表达式树(直到转换标准化):

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

以下摘录摘自C# Language Specification 4.0(在所有情况下都加重了重点)。

从“4.6表达式树类型”:

  

泛型类型Expression<D>的确切定义以及将匿名函数转换为表达式树类型时构造表达式树的精确规则都在本规范的范围之外< / strong>,并在别处描述。

从“6.5.2评估匿名函数转换到表达式树类型”:

  

将匿名函数转换为表达式树类型会生成表达式树(第4.6节)。更确切地说,对匿名函数转换的评估导致构造表示匿名函数本身的结构的对象结构。 表达式树的精确结构以及创建它的确切过程是实现定义的。

“6.5.3实现示例”中的第三个示例演示了捕获局部变量的匿名函数的转换,并确认了我的问题中提到的变量提升:

  

现在必须将局部变量的生命周期延长到至少匿名函数委托的生命周期。这可以通过将局部变量“提升”到编译器生成的类的字段中来实现。然后,局部变量(第7.15.5.2节)的实例化对应于创建编译器生成的类的实例,访问局部变量对应于访问编译器生成的类的实例中的字段

本节末尾进一步证实了这一点:

  

在将匿名函数转换为表达式树时,也可以使用此处应用于捕获局部变量的相同技术:对编译器生成的对象的引用可以存储在表达式树中,并且可以表示对局部变量的访问作为对这些对象的字段访问。这种方法的优点是它允许在代理和表达式树之间共享“提升的”局部变量。

但是,该部分的开头有一个免责声明:

  

此处描述的实现基于Microsoft C#编译器使用的相同原则,但它绝不是强制实现,也不是唯一可能的。它只是简单地提到了对表达式树的转换,因为它们的确切语义超出了本规范的范围。

P.S。 Eric Lippert confirms in this comment表达树规格从未发货过。 CodePlex上的DLR文档下存在Expression Trees v2 Spec,但其范围似乎并未涵盖C#中匿名函数到表达式树的转换。

答案 1 :(得分:4)

这是 所依赖的行为。

查看Abuse of C# lambda expressions or Syntax brilliance?

现在阅读来自C#设计团队的Eric Lippert的评论。它们包括:

  

我刚问安德斯(以及设计团队的其他成员)他们是什么   思想。我们只是说结果不能打印出来   适合家庭的报纸

  

至于为什么这是可怕的,我们可以从不明显,聪明开始   (记住,聪明是坏的,聪明的代码很难维护),完全没有   在lambda的设计者设想的设计用例中,   缓慢,脆弱,不可移植和不必要的

从这些陈述中我会说它不会成为记录或支持的行为。

答案 2 :(得分:1)

AFAIK,这是一个实施细节。

但是,我认为你可以打赌它实际上不会改变。

我刚刚在VS2012 RC中进行了测试,它按预期工作 - 所以至少你可以安全使用几年。