5

Duplicate

I can do this:

Func<CategorySummary, decimal> orderByFunc;
if (orderBy == OrderProductsByProperty.Speed)
     orderByFunc = x => x.Speed;
else
    orderByFunc = x => x.Price;

Why can't I do this:

Func<CategorySummary, decimal> orderByFunc = (orderBy == OrderProductsByProperty.Speed) ? x => x.Speed : x => x.Price;
Community
  • 1
  • 1
pondermatic
  • 6,453
  • 10
  • 48
  • 63
  • The language specification requires that the type _of the conditional expression_ be known. The type of the conditional expression depends on the types of the consequence and alternative, but in your case neither of them have a type -- lambdas are typeless. – Eric Lippert May 17 '09 at 14:49
  • 3
    Possible duplicate of [Lambdas and the conditional operator, weird issue.](http://stackoverflow.com/questions/263151/lambdas-and-the-conditional-operator-weird-issue) – Andrei Oct 15 '15 at 17:35

4 Answers4

6

The 'type inference' on the conditional operator is not quite good enough, I get a message like

Type of conditional expression cannot be determined because there is no implicit conversion between 'lambda expression' and 'lambda expression'

you can always just be explicit on the right-hand-side, a la

var o = true ? new Func<int,int>(x => 0) : new Func<int,int>(x => 1);

In any case it's just a minor annoyance regarding how the types of lambdas, type inference, and the conditional operator interact.

Brian
  • 117,631
  • 17
  • 236
  • 300
3

Just cast the lambda's to Func<CategorySummary, decimal> and it will work

bashmohandes
  • 2,356
  • 1
  • 16
  • 23
1

An alternative to the suggestions so far - move the conditional inside the lambda expression:

Func<CategorySummary, decimal> orderByFunc = 
    x => (orderBy == OrderProductsByProperty.Speed) ? x.Speed : x.Price;

It may not be suitable in all situations (and it does mean that the check is performed on every invocation) but sometimes it could be useful.

EDIT: As Eric points out, the two are not equivalent. Here's a quick example of how they differ (using explicit casting to get the conditional to work where the operands are lambdas):

using System;

class Test
{   
    static void Main()
    {
        bool likesCheese = false;

        Action outerConditional = likesCheese 
            ? (Action) (() => Console.WriteLine("Outer: I like cheese"))
            : (Action) (() => Console.WriteLine("Outer: I hate cheese"));

        Action innerConditional = () => 
            Console.WriteLine (likesCheese ? "Inner: I like cheese" 
                                           : "Inner: I hate cheese");


        Console.WriteLine("Before change...");
        outerConditional();
        innerConditional();

        likesCheese = true;
        Console.WriteLine("After change...");
        outerConditional();
        innerConditional();
    }
}

Results:

Before change...
Outer: I hate cheese
Inner: I hate cheese
After change...
Outer: I hate cheese
Inner: I like cheese

As you can see, the change to the value of likesCheese only affects the version which has the conditional operator inside the lambda expression. Sometimes this is desirable, sometimes not... but you definitely need to be aware of it.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Note that by doing so you might change the semantics of the program. The original program makes the choice using the value of "orderBy" at the time the delegate is CREATED. Your version uses the value of "orderBy" at the time the delegate is EXECUTED, when it might be different. – Eric Lippert May 17 '09 at 14:46
0

It's enough to cast only one resulting operand to a target type:

Action showResult = true 
    ? (Action)(() => Console.Write("Hello!"))
    : () => Console.Write("");

It was a little unexpected, but this is how compiler works. And when I think about it, it makes sense now.

Andrei
  • 42,814
  • 35
  • 154
  • 218