Reputation: 469
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?
Upvotes: 1
Views: 361
Reputation: 469
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 */});
Upvotes: 0
Reputation: 3497
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.
Upvotes: 2