Question

Why can I do this?

if (integerList.Direction == "ascending") { 
    integerList.Integers.OrderBy(i => i.IntegerValue);
} else{
    integerList.Integers.OrderByDescending(i => i.IntegerValue);
}

But not this?

integerList.Direction == "ascending" ? integerList.Integers.OrderBy(i => i.IntegerValue)
                                     : integerList.Integers.OrderByDescending(i => i.IntegerValue);

It returns the error:

only assignment, call, increment, decrement, await and new object expressions can be used as a statement.
Was it helpful?

Solution

Because this code makes a call to a given operation (regardless of the branch) which is in itself a statement, thus allowed,

if (integerList.Direction == "ascending") { 
    integerList.Integers.OrderBy(i => i.IntegerValue);
} else{
    integerList.Integers.OrderByDescending(i => i.IntegerValue);
}

Using the ternary operator, however, you end up with an expression, which by itself it is not allowed, as it is considered a no-op, except of course, like the error says, it is a new object expression,

integerList.Direction == "ascending" 
    ? integerList.Integers.OrderBy(i => i.IntegerValue)
    : integerList.Integers.OrderByDescending(i => i.IntegerValue);

The result of the operation it is not actually consumed, so the entire OrderBy is wasted.

Try this instead,

var intergersOrdered = 
    integerList.Direction == "ascending" 
        ? integerList.Integers.OrderBy(i => i.IntegerValue)
        : integerList.Integers.OrderByDescending(i => i.IntegerValue);

which turns the ternary operator into an assignment statement.

OTHER TIPS

The ternary operator in C# is an operator not a statement. You need to use it in a statement with something to receive the return value.

Read this answer by Jon Skeet.

Because by definition a ternary operator is an assignment operator, or more accurately a conditional assignment operator. It assigns different values according according to the supplied condition. It doesn't execute statements. While you can definitely unfold any ternary construct into an if-then-else construct, the reverse is not true.

While the fact remains that the ternary operator is a conditional assignment operator, you could probably still do this

var values = integerList.Direction == "ascending" ? integerList.Integers.OrderBy(i => i.IntegerValue)
                                     : integerList.Integers.OrderByDescending(i => i.IntegerValue);

An easy way to think about the difference between if-else and the ternary operator:

  • If-else allows you to decide which block of code will be executed.
  • The ternary operator helps you decide between two objects or values (of the same type) to use inside a single statement.

The reason you are getting a compiler error for your ternary operator is because the rules of the language: the Ternary operator returns a value. But values are used in statements; they are not themselves statements. Consider:

int x = 1;

// Both these lines are invalid because a string literal is not a complete statement:
"x is an integer";
x < 0 ? "x is Negative" : "x is not Negative";

//Valid:
string intDescription = "x is an integer";
string describeNegative = x < 0 ? "x is Negative" : "x is not Negative";

// Invalid because the ternary operator chooses between values, and Console.WriteLine
// is void:
x < 0 ? Console.WriteLine("x is Negative") : Console.WriteLine("x is not Negative");

// Valid:
Console.WriteLine(x < 0 ? "x is Negative" : "x is not Negative");
// or:
string describeNegative = x < 0 ? "x is Negative" : "x is not Negative";
Console.WriteLine(describeNegative);

Now hopefully you understand the Ternary operator a bit better. Unfortunately, there's another problem with your code. Even though this compiles, it doesn't actually do anything:

if (integerList.Direction == "ascending") { 
    integerList.Integers.OrderBy(i => i.IntegerValue);
} else{
    integerList.Integers.OrderByDescending(i => i.IntegerValue);
}

OrderBy creates a new object that is an IOrderedEnumerable<YourIntegerClass> and contains the same contents that Integers did, but sorted. OrderBy returns this new object and leaves your original container unchanged. This is a relatively common pattern with the Linq extension methods, although most return IEnumerable objects instead of IOrderedEnumerable.

Instead of only making a copy that you don't do anything with, I'm guessing you want to sort the contents of Integers; to do that, you need to assign that result of the OrderBy back to the Integers object:

if (integerList.Direction == "ascending") { 
    //I'm guessing that Integers is a List, so I've added .ToList() as well here:
    integerList.Integers = integerList.Integers.OrderBy(i => i.IntegerValue).ToList();
} else{
    integerList.Integers = integerList.Integers.OrderByDescending(i => i.IntegerValue).ToList();
}

Because you are choosing between two values (in this case, for assignment to Integers) you can use the ternary operator:

integerList.Integers = integerList.Direction == "ascending" 
    ? integerList.Integers.OrderBy(i => i.IntegerValue).ToList()
    : integerList.Integers.OrderByDescending(i => i.IntegerValue).ToList();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top