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

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

public static string GetVariableName(Expression<Func> 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#编译器将在匿名函数中捕获的任何局部变量(和参数)提升为幕后编译器生成的类中的同名实例变量(根据Jon Skeet )。 如果不是这种情况, BodyMemberExpression将失败,因为MemberExpression表示字段或属性访问。

这个变量是促销记录的行为,还是一个实现细节可能会在框架的其他版本中发生变化?

注意:这个问题是我前一个关于参数validation的概括。

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

似乎我的问题的答案是否定的 ; 该function是非标准化的。 这种情况似乎比我原先怀疑的更加惨淡; 不仅是捕获变量的推广是非标准化的,而且将匿名函数转换为表达式树表示的整个规范也是如此。

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

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

以下摘录摘自C#语言规范4.0 (在所有情况下都加重了)。

从“4.6表达式树类型”:

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

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

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

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

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

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

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

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

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

PS Eric Lippert 在这篇评论中证实表达树规格从未发货。 在CodePlex上的DLR文档下存在Expression Trees v2 Spec ,但其范围似乎并未涵盖C#中匿名函数到表达式树的转换。

这是你不应该依赖的行为。

看一下滥用C#lambda表达式或Syntax辉煌?

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

我刚问安德斯(以及设计团队的其他成员)他们的想法。 我们只是说结果不适合在家庭友好的报纸上刊登

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

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

AFAIK,它是一个实现细节。

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

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