19

I have a function that has the following signature...

public string DoJunk(Expression<Func<bool>> expression)

I'm trying to find a way to convert the "expression" parameter back to something resembling the original source code (or at least a c# representation of the original souce code). So, if someone calls the function like this...

DoJunk(() => (i + j) * 9 == Math.Round((double)j / (i - 3), 4))

...I'd like to be able to convert the expression to this...

(i + j) * 9 == Math.Round((double)j / (i - 3), 4)

Has anyone done this?

herbrandson
  • 2,357
  • 2
  • 30
  • 44
  • I'm experimenting with some ideas for a unit testing framework. My idea is that if you test against a lambda and the test fails, you could actually show the code that failed. For example, instead of getting "expected: 4 actual: 5", you could get a message more like "expected: user.Age == 4 actual: user.Age == 5" – herbrandson Sep 10 '09 at 20:57
  • You ask for a C#-like string representation, but I've written [a library that outputs other representations](https://github.com/zspitz/ExpressionTreeToString): Visual Basic - like, factory methods needed to create a similar expression; object/collection initialization syntax. – Zev Spitz Dec 26 '19 at 01:03

2 Answers2

14

I've just happened across this; I've written a free, open-source library which provides an extension method to create a source-code-like string from an Expression:

using AgileObjects.ReadableExpressions;

var myExpression = CreateBigExpressionTree();
var expressionSource = myExpression.ToReadableString();

I've written a blog about it, the source is on GitHub, there's a NuGet package containing the extension method, and I've written a set of Debugger Visualizers for VS 2010 -> 2022 which are in the Visual Studio Marketplace.

Steve Wilkes
  • 7,085
  • 3
  • 29
  • 32
9

Here's an interesting article, with code, discussing the conversion of expression trees back into something that resembles (roughly) the original source:

Expression Trees-Lambdas to CodeDom Conversion

As a side-note, have you tried calling the expression's ToString method?

Expression<Func<int, int, bool>> expr =
    (i, j) => (i + j) * 9 == Math.Round((double)j / (i - 3), 4);

Console.WriteLine(expr.ToString());
// (i, j) => (Convert(((i + j) * 9)) = Round((Convert(j) / Convert((i - 3))), 4))

Console.WriteLine(expr.Body.ToString());
// (Convert(((i + j) * 9)) = Round((Convert(j) / Convert((i - 3))), 4))
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • 1
    I have tried using ToString(), but it gives me something pretty nasty looking... (Convert(((value(LambdaToStringSpike.Program+<>c__DisplayClass0).i + value(LambdaToStringSpike.Program+<>c__DisplayClass0).j) * 9)) = Round((Convert(value(LambdaToStringSpike.Program+<>c__DisplayClass0).j) / Convert((value(LambdaToStringSpike.Program+<>c__DisplayClass0).i - 3))), 4)) – herbrandson Sep 10 '09 at 17:24
  • 1
    Yep, I think the output you're seeing is what's generated behind-the-scenes by the C# compiler to handle the captured variables `i` and `j`. In my examples, `i` and `j` are locals, so the output is much closer to the original source code. – LukeH Sep 10 '09 at 20:28