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

C#.NetLambdaExpressionFunc

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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

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 - C#

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

Solution 3 - C#

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 - C#

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 - C#

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.

Solution 6 - C#

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

Solution 7 - C#

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

Solution 8 - C#

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

Solution 9 - C#

Change

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

To

// This next line works!
Expression<Func<T>> DangerousExpression = () => dangerousCall();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDave CameronView Question on Stackoverflow
Solution 1 - C#mmxView Answer on Stackoverflow
Solution 2 - C#OverrideView Answer on Stackoverflow
Solution 3 - C#David WengierView Answer on Stackoverflow
Solution 4 - C#SagiView Answer on Stackoverflow
Solution 5 - C#Marc GravellView Answer on Stackoverflow
Solution 6 - C#Steve WillcockView Answer on Stackoverflow
Solution 7 - C#Dmitry DzyginView Answer on Stackoverflow
Solution 8 - C#aaguiarView Answer on Stackoverflow
Solution 9 - C#mheymanView Answer on Stackoverflow