【问题标题】:How to capture an expression tree in C#如何在 C# 中捕获表达式树
【发布时间】:2018-08-11 06:01:35
【问题描述】:

我正在编写一些调试/测试代码,我想在其中显示原始表达式并评估它,用于任意表达式。

对于(琐碎的)示例:

IList<string> myString = /* Some string collection expression */

ShowMe(myString.Select(s => s.ToLower()));

我在哪里实现 ShowMe 类似的东西:

public void ShowMe(/* Not sure what has to go here */)
{
    /* Not sure what has to go here */
    Console.WriteLine(expression.ToString();
    IEnumerable result = expression.Evaluate(); // or something
    foreach(item in result)
    {
        Console.WriteLine(/* etc. */)
    }
}

结果将被写入控制台:

myString.Select(s => s.ToLower())

(第一项)

(下一项

(等等...)

换句话说,我的ShowMe 方法对表达式树而不是表达式的值进行操作,因此它既可以显示给出的表达式,也可以显示计算结果。

我不能简单地将ShowMe 声明为:

public void ShowMe(Expression expr)

...但如果我声明为

public void ShowMe(Expression<Func<Enumerable>> expr)

...它 sort-of 有效 - 我必须使用 lambda 表达式调用我的方法:

ShowMe(() => myString.Select(s => s.ToLower()))

...我不想这样做。

我有理由相信这是可以做到的...... FluentAssertions 做到了。例如:如果我执行下面这行测试代码:

(1 + 1).Should.Be(3)

我得到以下结果:

预期 (1 + 1) 为 3,但实际为 2。

FluentAssertion 已经评估了表达式 (1+1) 并捕获了表达式树,以便能够显示它评估的原始表达式。

我看不出这是如何做到的,但我想做类似的事情。我该怎么做?

【问题讨论】:

    标签: c# expression-trees


    【解决方案1】:

    这本身是任何方法都无法实现的。

    所有这些库只是解析堆栈跟踪并提取文件名和行号。然后从给定行的源代码文件中提取表达式(虽然这包括一些解析/验证)。

    同样值得注意的是,如果源代码不存在/不可用,则无法显示表达式。

    【讨论】:

    【解决方案2】:

    想出了一个可以接受的折衷方案:

    public static class ObjectHelper
    {
        public static void ToConsole<T>(this IEnumerable<T> enumerable, Expression<Func<T,object>> expr)
            where T:class
        {
            var fn = expr.Compile();
    
            var result = enumerable.Select(s => fn(s));
    
            Console.WriteLine($"My data selected as {PrettyPrintExpression(expr)}");
            foreach(var element in result)
            {
                Console.WriteLine(/*  etc.  */);
            }
        }
    
        private static string PrettyPrintExpression(Expression<Func<T,object>> expr)
        {
            // Walk the expression tree to print as desired
        }
    }
    

    ...我可以调用为:

    IList<MyObject> list = /* etc. */
    list.ToConsole(s => new{/* any members I want out of MyObject */});
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-11
      • 1970-01-01
      • 2014-02-04
      • 1970-01-01
      • 2010-11-25
      • 2019-06-05
      相关资源
      最近更新 更多