How to capture an expression tree in C#

c# expression-trees

Question

I am writing some debug/test code in which I want to both display the original expression and evaluate it, for any arbitrary expression.

For (trivial) example:

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

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

Where I implement ShowMe something like:

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. */)
    }
}

And the result will be written to the console as:

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

(first item)

(next item

(etc...)

In other words, my ShowMe method operates on the expression tree rather than the value of the expression, so that it can both display the expression it was given as well as the computed result.

I can't simply declare ShowMe as:

public void ShowMe(Expression expr)

... but if I declare is as

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

... it sort-of works - I have to call my method with a lambda expression thus:

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

... which I don't want to do.

I'm reasonably sure this can be done... FluentAssertions does it. For example: if I execute the following line of test code:

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

I get the following result:

Expected (1 + 1) to be 3, but found 2.

The FluentAssertion has both evaluated the expression (1+1) and captured the expression tree so that was able to display the original expression it evaluated.

I can't see how this was done, but I want to do similar. How do I do it?

1
1
8/10/2018 6:12:02 PM

Accepted Answer

This is per se not possible with any method.

All those libraries just parse the stack trace and extract the file name as well as the line number. Then the expression is extracted from the source code file at the given line (whilst this includes some parsing/validation).

It is also notably, that the expression can't be shown, if the source code does not exists/is not available.

2
8/10/2018 6:43:55 PM

Popular Answer

Figured out an acceptable compromise:

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
    }
}

...which I can invoke as:

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


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow