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

标签 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# 编译器将匿名函数中捕获的任何局部变量(和参数)提升为编译器在幕后生成的类中的同名实例变量(根据 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/

相关文章:

c# - 从 XSD 生成 SQL Server 数据库

java - CompletableFuture 没有按预期工作

c++ - 为什么在某些 STL 容器中我们不能直接使用 lambda 而不提及它的类型?

c# - 将html内容保存到数据库

c#语法帮助,关于泛型编程

c# - 使用简单注入(inject)器解决通用接口(interface)

.net - WinDbg/SOS : How to correlate managed threads from ! 带有 System.Threading.Thread 实例的线程命令

c# - 无法在 Visual Studio 中为 UWP 应用程序设置 "User Notification Listener"功能

c# - 仅出于内部函数中异常报告的目的包含参数

python - 将 lambda 表达式应用于数组元素时出现 ValueError