我有兴趣以重构安全的方式在运行时检索局部变量(和参数)的名称。我有以下扩展方法:
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# 编译器将匿名函数中捕获的任何局部变量(和参数)提升为编译器在幕后生成的类中的同名实例变量(根据 Jon Skeet )。如果不是这种情况,Body
的类型转换至 MemberExpression
会失败,因为 MemberExpression
表示字段或属性访问。
这个变量提升是否记录了行为,或者它是一个在其他版本的框架中可能会发生变化的实现细节?
注意:这个问题是my former one on argument validation的概括.
最佳答案
Update: This is no longer an issue from C# 6, which has introduced the
nameof
operator to address such scenarios (see MSDN).
看来我的问题的答案是否;该功能是非标准化的。情况似乎比我最初怀疑的还要惨淡;不仅捕获变量的提升是非标准化的,而且将匿名函数转换为其表达式树表示的整个规范也是如此。
这意味着即使是简单的匿名函数,如下所示,也不能保证在框架的不同实现中产生一致的表达式树(直到转换标准化):
Expression<Func<int, int, int>> add = (int x, int y) => x + y;
以下摘录自 C# Language Specification 4.0 (在所有情况下都添加了重点)。
来自“4.6 表达式树类型”:
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.
来自“6.5.2 匿名函数转换为表达式树类型的评估”:
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.
“6.5.3 实现示例”中的第三个示例演示了捕获局部变量的匿名函数的转换,并证实了我问题中提到的变量提升:
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.
本节末尾进一步证实了这一点:
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.
但是,在该部分的开头有免责声明:
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.
附言埃里克·利珀特 confirms in this comment表达式树规范从未发布。存在一个 Expression Trees v2 Spec在 CodePlex 上的 DLR 文档下,但其范围似乎并未涵盖将匿名函数转换为 C# 中的表达式树。
关于c# - 通过 lambda 表达式在运行时获取局部变量(和参数)的名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11063502/