converting a .net Func<T> to a .net Expression<Func<T>>
Asked Answered
R

9

142

Going from a lambda to an Expression is easy using a method call...

public void GimmeExpression(Expression<Func<T>> expression)
{
    ((MemberExpression)expression.Body).Member.Name; // "DoStuff"
}

public void SomewhereElse()
{
    GimmeExpression(() => thing.DoStuff());
}

But I would like to turn the Func in to an expression, only in rare cases...

public void ContainTheDanger(Func<T> dangerousCall)
{
    try 
    {
        dangerousCall();
    }
    catch (Exception e)
    {
        // This next line does not work...
        Expression<Func<T>> DangerousExpression = dangerousCall;
        var nameOfDanger = 
            ((MemberExpression)dangerousCall.Body).Member.Name;
        throw new DangerContainer(
            "Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    ContainTheDanger(() => thing.CrossTheStreams());
}

The line that does not work gives me the compile-time error Cannot implicitly convert type 'System.Func<T>' to 'System.Linq.Expressions.Expression<System.Func<T>>'. An explicit cast does not resolve the situation. Is there a facility to do this that I am overlooking?

Romanticist answered 20/4, 2009 at 10:40 Comment(4)
I don't really see much use for the 'rare case' example. The caller is passing in the Func<T>. There is no need to repeat back to the caller what that Func<T> was (via the exception).Acrilan
The exception is not handled in the caller. And, because there are multiple call sites passing in different Func<T>s, catching the exception in the caller creates duplication.Romanticist
The exception stack trace is designed to show this information. If the exception is thrown within the invocation of the Func<T>, this will show in the stack trace. Incidentally, if you were to choose to go the other way, i.e. accept an expression and compile it for invocation, you would lose this since the stack trace would show something like at lambda_method(Closure ) for the invocation of the compiled delegate.Acrilan
I guess you should look at the answer in this [link][1] [1]: https://mcmap.net/q/112607/-create-expression-from-func/…Biparty
O
115

Ooh, it's not easy at all. Func<T> represents a generic delegate and not an expression. If there's any way you could do so (due to optimizations and other things done by the compiler, some data might be thrown away, so it might be impossible to get the original expression back), it'd be disassembling the IL on the fly and inferring the expression (which is by no means easy). Treating lambda expressions as data (Expression<Func<T>>) is a magic done by the compiler (basically the compiler builds an expression tree in code instead of compiling it to IL).

Related fact

This is why languages that push lambdas to the extreme (like Lisp) are often easier to implement as interpreters. In those languages, code and data are essentially the same thing (even at run time), but our chip cannot understand that form of code, so we have to emulate such a machine by building an interpreter on top of it that understands it (the choice made by Lisp like languages) or sacrificing the power (code will no longer be exactly equal to data) to some extent (the choice made by C#). In C#, the compiler gives the illusion of treating code as data by allowing lambdas to be interpreted as code (Func<T>) and data (Expression<Func<T>>) at compile time.

Overexcite answered 20/4, 2009 at 10:43 Comment(3)
Lisp doesn't have to be interpreted, it can easily be compiled. Macros would have to be expanded at compile time, and if you want to support eval you would need to start up the compiler, but other than that, there's no problem at all doing that.Lussi
"Expression<Func<T>> DangerousExpression = () => dangerousCall();" is not easy?Rosalia
@Rosalia That would create new Expression about your wrapper action, but it would have no expression tree info about internals of dangerousCall delegate.Hazing
T
49
    private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)  
    {  
        return x => f(x);  
    } 
Those answered 27/2, 2011 at 13:48 Comment(5)
I wanted to traverse the syntax tree of the returned expression. Would this approach allow me to do that?Romanticist
@DaveCameron - No. See above answers - the already compiled Func will be hidden in a new Expression. This simply adds one layer of data over code; you could traverse one layer just to find your parameter f without further details, so you're right where you started.Ourselves
It causes client evaluation exceptions for entity framework core.Kessia
@Kessia were you able to solve the exception? Thanks in advance!Herod
@Luca, You should change the statement to what is understandable to EF. (e.g. change .Equal(<ignore casing parameter>) to == x.ToLower()Kessia
S
26

What you probably should do, is turn the method around. Take in an Expression>, and compile and run. If it fails, you already have the Expression to look into.

public void ContainTheDanger(Expression<Func<T>> dangerousCall)
{
    try 
    {
        dangerousCall().Compile().Invoke();;
    }
    catch (Exception e)
    {
        // This next line does not work...
        var nameOfDanger = 
            ((MemberExpression)dangerousCall.Body).Member.Name;
        throw new DangerContainer(
            "Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    ContainTheDanger(() => thing.CrossTheStreams());
}

Obviously you need to consider the performance implications of this, and determine if it is something that you really need to do.

Shanaeshanahan answered 20/4, 2009 at 10:53 Comment(0)
P
12

NJection.LambdaConverter is a library that converts a delegate to an expression

public class Program
{
    private static void Main(string[] args) {
       var lambda = Lambda.TransformMethodTo<Func<string, int>>()
                          .From(() => Parse)
                          .ToLambda();            
    }   
        
    public static int Parse(string value) {
       return int.Parse(value)
    } 
}
Phylum answered 2/9, 2012 at 17:29 Comment(0)
N
11

If you sometimes need an expression and sometimes need a delegate, you have 2 options:

  • have different methods (1 for each)
  • always accept the Expression<...> version, and just .Compile().Invoke(...) it if you want a delegate. Obviously this has cost.
Nook answered 20/4, 2009 at 10:52 Comment(0)
C
8

You can go the other way via the .Compile() method however - not sure if this is useful for you:

public void ContainTheDanger<T>(Expression<Func<T>> dangerousCall)
{
    try
    {
        var expr = dangerousCall.Compile();
        expr.Invoke();
    }
    catch (Exception e)
    {
        Expression<Func<T>> DangerousExpression = dangerousCall;
        var nameOfDanger = ((MethodCallExpression)dangerousCall.Body).Method.Name;
        throw new DangerContainer("Danger manifested while " + nameOfDanger, e);
    }
}

public void SomewhereElse()
{
    var thing = new Thing();
    ContainTheDanger(() => thing.CrossTheStreams());
}
Celestyn answered 20/4, 2009 at 10:55 Comment(0)
S
7
 Expression<Func<T>> ToExpression<T>(Func<T> call)
        {
            MethodCallExpression methodCall = call.Target == null
                ? Expression.Call(call.Method)
                : Expression.Call(Expression.Constant(call.Target), call.Method);

            return Expression.Lambda<Func<T>>(methodCall);
        }
Saddletree answered 21/3, 2013 at 12:38 Comment(3)
Can you elaborate the "this will not work" part? Have you actually tried compiling and executing it? Or it doesn't work particularly in your application?Saddletree
FWIW, this might not be what the main ticket was about, but it was what I needed. It was the call.Target part that was killing me. It worked for years, and then suddenly stopped working and started complaining about a static/non-static blah blah. Anyway, thanks!Gert
I've been using Override's answer for years but my expression now contains a Span<char> which won't work. This is essentially the same thing but works with Span<char>.Coxcombry
V
3

JB Evain from the Cecil Mono team is doing some progress to enable this

http://evain.net/blog/articles/2009/04/22/converting-delegates-to-expression-trees

Ventura answered 1/5, 2009 at 19:38 Comment(0)
R
-1

Change

// This next line does not work...
Expression<Func<T>> DangerousExpression = dangerousCall;

To

// This next line works!
Expression<Func<T>> DangerousExpression = () => dangerousCall();
Rosalia answered 17/2, 2014 at 21:22 Comment(1)
Servy, it as absolutely legal way to get an expression. syntax sugar to build it though expression.lambda and expression.call. Why do you think it should fail at runtime?Caldron

© 2022 - 2024 — McMap. All rights reserved.