Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning a Func to an Expression and vice versa

I was tampering with Expressions and I got confused at some points

  1. We can assign same LamdaExpression to both Expression and/or Func. But we cannot assign a Func to an Expression (or an Expression to Func). Why cannot we do that? I looked for if a conversion operator between Expression and Func is defined but I could not found any.

    Func<int, int> sumFunc            = i => i + i;
    Expression<Func<int, int>> sumExp = i => i + i;
    
    // sumExp = sumFunc; // Cannot convert source type 'System.Func<int,int>' to target type 'System.Linq.Expressions.Expression<System.Func<int,int>>'
    // sumFunc = sumExp; // Cannot convert source type 'System.Linq.Expressions.Expression<System.Func<int,int>>' to target type 'System.Func<int,int>'
    
  2. Even we cannot assign a LambdaExpression to an object. Again, why cannot we do that?

    // object o = i => i + i; // Cannot convert source type 'lambda expression' to target type 'object'
    
  3. I think there is something about compiler. If so, can we write our custom types those behave in this (confusing) manner and take advantage of something.

like image 634
Mehmet Ataş Avatar asked Jan 05 '13 19:01

Mehmet Ataş


People also ask

What is with expression in C#?

An expression in C# is a combination of operands (variables, literals, method calls) and operators that can be evaluated to a single value. To be precise, an expression must have at least one operand but may not have any operator.


2 Answers

Regarding to the C# Language Specification a lambda expression like

i => i + i

is an anonymous function. An expression with this classification can be implicitly converted to a compatible delegate type or expression tree type. This is why you can write both

Func<int, int> sumFunc            = i => i + i;
Expression<Func<int, int>> sumExp = i => i + i;

The first is a delegate type, the second an expression tree type. Because there is no implicit converting between those types, you cannot assign sumFunc = sumExp or vice versa. But since the expression tree sumExp represents a lambda expression, you can compile that expression to an executable delegate and assign it to sumFunc, because this is such a compatible delegate:

sumFunc = sumExp.Compile();

The other direction is not possible, because a delegate cannot easily be "decompiled" into an expression tree.

The reason why you cannot write

object o = i => i + i;

is, that an anonymous function does not have a value or type in and of itself, it is just convertible to a delegate or expression tree type. You have to tell the compiler which of both you want, so you can convert it at first and then assign the result to a variable of type object:

object sumFuncObject = (Func<int, int>) (i => i + i);
object sumExpObject = (Expression<Func<int, int>>) (i => i + i);

Regarding your last question: You can create custom implicit or explicit conversions between complex types, so that this "magic" can be applied to assignments. See the Conversion Operations Programming Guide for more information.

like image 147
Desty Avatar answered Oct 16 '22 16:10

Desty


Actually both of these expressions are syntax sugar, transformed by compiler in different language constructions.

When you write lambda expression, compiler does this: makes member function that matches your lamda expression and assigns it to your sumFunc variable (this is not exact code, just to get an idea):

class Program
{
   private static int generatedname(int i)
   {
        return i + i;
   }

   static void Main()
   {
       Func<int, int> sumFunc = generatedname;
   } 
}

When you write Expression tree there is even more magic happens. At compile time compiler transforms your expression into Expression tree "construction". Like this.

class Program
{
    static void Main()
    {
        var addPrm = Expression.Parameter(typeof(int), "i");
        Expression<Func<int, int>> sumExp = 
            Expression.Lambda<Func<int, int>>(
                Expression.Add(
                    addPrm,
                    addPrm
                ),
                addPrm
            );
    } 
}

You see this is completely different things, thats why you can not simply cast one to another.

like image 42
Dima Avatar answered Oct 16 '22 16:10

Dima