Search code examples
c#lambdaimplicit-conversion

How do lambda expressions work with implicit casting?


I'm a bit confused on how C# handles lambda expression with regard to implicit casting.

My goal is similar to the following minimal example:

A<int>.DoSomething(x => x == 42);

public static class A<T>
{
    public static void DoSomething(PredicateAdapter<T> _) { }
}

public class PredicateAdapter<T>
{
    public static implicit operator PredicateAdapter<T>(Predicate<T> _) => new();
}

So I'd like to create a wrapper for predicates, that allows me to do fancy stuff (which is not listed here in this example for the sake of brevity.) However, the code above does not compile, because "Error CS1660 : Cannot convert lambda expression to type 'PredicateAdapter' because it is not a delegate type".

In contrast, here is an example where implicit casting works:

A<int>.DoSomething(x => x == 42);

public static class A<T>
{
    public static void DoSomething(Predicate<T> _) { }
}

The compiler is able to infer that the lambda expression x => x == 42 can be implicitly cast to Predicate<int> and passes this happily along to A<int>.DoSomething(Predicate<int>).

Also, explicitly casting the lambda to Predicate<int> means my custom implicit casting is applied and works:

A<int>.DoSomething((Predicate<int>)(x => x == 42));

public static class A<T>
{
    public static void DoSomething(PredicateAdapter<T> _) { }
}

public class PredicateAdapter<T>
{
    public static implicit operator PredicateAdapter<T>(Predicate<T> _) => new();
}

So, my question is: Why do I need to cast the lambda explicitly (and write much less readable code), when C# is apparently able to do this cast implicitly? And: Is it somehow possible to modify the classes A and PredicateAdapter so that I can use a simple lambda expression (without any explicit casting) and still use the PredicateAdapter?


Solution

  • Based on my understanding it is "because the specification says so". As 12.19 Anonymous function expressions section states:

    An anonymous function does not have a value or type in and of itself, but is convertible to a compatible delegate or expression-tree type.

    So when you are using anonymous function (lambda expression) compiler should decide what type it should be (since you are not specifying type explicitly). Since you have a parameter which is not a delegate type (like Predicate<T>) or expression tree type (i.e. Expression<Func<T, TResult>>) the compiler produces error.

    Also from 10.7 Anonymous function conversions:

    An anonymous_method_expression or lambda_expression is classified as an anonymous function (§12.19). The expression does not have a type, but can be implicitly converted to a compatible delegate type. Some lambda expressions may also be implicitly converted to a compatible expression tree type.

    Also Lambda improvements in C# 10 can be an interesting read.