23

Suppose I have a class:

class MyClass {
    public int MyMethod(Func<int, int> f) { return 0; }
    public int MyMethod(Expression<Func<int, int>> f) { return 1; }
}

When I try to call the method with a lambda expression, I get a compile error stating that the call is ambiguous between the two overloads:

var myClass = new MyClass();
myClass.MyMethod(x => 1 + x); // Error!

while, of course, calling with an explicit type works fine:

myClass.MyMethod((Func<int, int>)(x => 1 + x)); // OK, returns 0
myClass.MyMethod((Expression<Func<int, int>>)(x => 1 + x)); // OK, returns 1

The expression tree contains more information (the actual code), and I may want to make use of this information when it is available. But I also want my code to work with delegates. Unfortunately, this ambiguity makes it so I have to find another way to distinguish between the two calls, which messes up an otherwise clean API.

The C# spec doesn't say anything about this specific situation, so in this sense the behavior does match the spec.

However, there is an argument to be made that the expression tree should be preferred over the delegate. The Compile method acts as an explicit conversion from an expression tree to a delegate. The expression tree contains more information, and when you compile to a delegate, you lose that information. There is no conversion in the other direction.

Are there any reasons not to prefer the expression tree?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Jeffrey Sax
  • 10,253
  • 3
  • 29
  • 40
  • 3
    The argument in favour of `Func` is that you can execute it directly without having to go through a relatively expensive compliation stage. – Lee Nov 26 '11 at 19:01

3 Answers3

6

Answering the question on the title, they are ambiguous because the type system has no concept of "lambda expression". It's a compiler feature which can be converted to a delegate or an expression tree so you need to be explicit to which type you want to convert it. Most of the times the target is also inferred automatically by the compiler because of the context in which the lambda expression is used. For example, the use of lambdas in IEnumerable extension methods versus the use of lambdas in IQueryable extension methods.

Now, to answer the question on why not always prefer the expression tree, you have the performance argument that MagnatLU already stated. If you accept an Expression and then call Compile to be able to execute it, then it will always be slower when compared to accepting a delegate.

There is also a semantic difference between the two, a delegate is just a way to execute some code while an expression tree is a description of the actual code.

If I were you I would opt to change the name of the method accepting the expression to something that clearly reflects what it does extra to the delegate based one.

João Angelo
  • 56,552
  • 12
  • 145
  • 147
  • 1
    The fact that a lambda expression can be interpreted in two ways does not in itself mean there couldn't be a rule to prefer one over the other. So I don't really like that part of your answer. The rest is great, though. Thanks! – Jeffrey Sax Nov 28 '11 at 02:47
2

How will compiler ever know whether to choose expression tree or delegate? It's your decision and not compiler's.

All linq methods provide both delegate and expressions as well, but they are extensions in the form of target type.

 Enumerable.Where<T>(this IEnumerable<T> en , Func<T,bool> filter)
 Queryable.Where<T>(this IQueryable<T> en , Expression<Func<T,bool>> filter)

So based on target type compiler makes choice. Compiler is a program, indeed it's machine and it's context free, it can not take decision like human on what might be better, it only follows rules and those rules needs to be unambiguous.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • 2
    The question is not why the compiler labels it as ambiguous: that's because it is in the spec by omission. The question is why the language designers left it ambiguous. – Jeffrey Sax Nov 26 '11 at 21:27
  • Why has no specific answer as every permutation and combination is not possible to design, this way there will be many such rules which are of low priority and rare usage scenario. It's not practical to guess and add all design rules. – Akash Kava Nov 27 '11 at 07:18
0

Building and compiling expression tree takes time and can put a significant pressure on GC. You shouldn't construct and compile expression at runtime unless you really really have to.

Edit: Also keep in mind that not every expression or method can be expressed as Expression<E>. Func<U, V> cares only about parameters' and return type and have no such limitations.

MagnatLU
  • 5,967
  • 1
  • 22
  • 17