converting a .net Func<T> to a .net Expression<Func<T>>

75,433

Solution 1

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.

Solution 2

    private static Expression<Func<T, bool>> FuncToExpression<T>(Func<T, bool> f)  
    {  
        return x => f(x);  
    } 

Solution 3

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.

Solution 4

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

Solution 5

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.
Share:
75,433
Dave Cameron
Author by

Dave Cameron

Updated on September 13, 2020

Comments

  • Dave Cameron
    Dave Cameron over 3 years

    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?

    • Adam Ralph
      Adam Ralph over 12 years
      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).
    • Dave Cameron
      Dave Cameron almost 12 years
      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.
    • Adam Ralph
      Adam Ralph almost 12 years
      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.
    • Ibrahim Kais Ibrahim
      Ibrahim Kais Ibrahim over 10 years
      I guess you should look at the answer in this [link][1] [1]: stackoverflow.com/questions/9377635/create-expression-from-f‌​unc/…
  • Dave Cameron
    Dave Cameron about 13 years
    I wanted to traverse the syntax tree of the returned expression. Would this approach allow me to do that?
  • configurator
    configurator almost 13 years
    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.
  • Jonno
    Jonno about 12 years
    @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.
  • mheyman
    mheyman about 10 years
    "Expression<Func<T>> DangerousExpression = () => dangerousCall();" is not easy?
  • Nenad
    Nenad over 9 years
    @mheyman That would create new Expression about your wrapper action, but it would have no expression tree info about internals of dangerousCall delegate.
  • Dmitry Dzygin
    Dmitry Dzygin over 8 years
    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?
  • Eli Gassert
    Eli Gassert over 7 years
    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!
  • Roman Pokrovskij
    Roman Pokrovskij over 6 years
    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?
  • Josh Close
    Josh Close over 3 years
    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>.
  • XAMT
    XAMT over 2 years
    It causes client evaluation exceptions for entity framework core.