PiousVenom
PiousVenom

Reputation: 6908

Why are if statement expressions not treated as conditions?

I have a combobox that has a list of dates in it. I want to ensure that the user actually selects a date. So, I've got the following:

        if (cmbDateSelecter.SelectedItem.ToString().ToLower().Contains("select") || 
            cmbDateSelecter.SelectedItem.ToString().ToLower().Contains("seleccione") || 
            cmbDateSelecter.SelectedItem == null)

The default item is "Select a date", so I'm checking to see if that's the selected item. This used to work on it's own, and for some reason that I haven't figured out yet, started throwing NullReferenceExceptions. So, I added the null check. However, I'm still getting the exception. But if I do:

if(cmbDateSelecter.SelectedItem == null)

Now, I know I can put the null check first, and everything will be hunky dory. My question is why does it not evaluate all expressions before throwing an exception? And if one of them is true, in the case of my expression(being the null check), why does it still throw an exception?

Upvotes: 1

Views: 104

Answers (3)

John Saunders
John Saunders

Reputation: 161773

The expression inside the parentheses of an if statement is just a normal expression of type bool. It is not a list of conditions.

It seems that you were introduced to the ifstatement the wrong way around. It is not a special case - it is an instance of a general case: statements in C# which accept an expression, then do something with the result of evaluating the expression. Other examples are:

  • return expression; - will return the value of the expression
  • while (expression) statement; will execute statement as long as the expression remains true
  • do statement; while (expression); executes statement as long as the expression remains true, but executes it at least once, even if the expression starts off false.

What these and many more have in common is that they all evaluate the expression to determine its value. If any exception is thrown during the evaluation of the expression, then the expression evaluation ceases immediately, and the search for an exception handler begins.

It pretty much works the same way in all modern programming languages, so this is a lesson that will stay with you your entire life.

Note that even something like "short-circuit evaluation" doesn't change this:

private bool NotGood(){throw new Exception();}

if (a || b)

This will first evaluate a. If a is true, then evaluation stops. Only if a is false will b ever be evaluated. Still,

if (NotGood() || b)

will throw an exception and will never evaluate b.


P.S. I have worked with systems which had different semantics for "conditionals". In particular, rules engines and other such systems process a declarative form of the behavior of the system. Such systems may do things like evaluate all parts of the conditional in parallel, or even treat an exception as a value, perhaps as false.

Upvotes: 1

to StackOverflow
to StackOverflow

Reputation: 124696

It evaluates left to right and uses short-circuit evaluation, so you need:

if (cmbDateSelecter.SelectedItem == null ||
    cmbDateSelecter.SelectedItem.ToString().ToLower().Contains("select") || 
    cmbDateSelecter.SelectedItem.ToString().ToLower().Contains("seleccione")
)

Another point is that your test is a bit fragile, as you're relying on a particular string for the "please select" option. Since this is no doubt the first option, you could instead use:

if (cmbDateSelector.SelectedIndex <= 0)
{
    ... nothing selected ...
}

Upvotes: 2

Jon
Jon

Reputation: 437376

It does not evaluate all conditions before throwing because in C# (and pretty much every other programming language) the logical OR operator does short-circuit evaluation. Even if it did evaluate them all it would still throw because evaluating any of the first two conditions involves trying to access a null object. So that kind of behavior would not offer a solution.

However, short-circuit evaluation is precisely what allows you to fix the problem by moving the null check at the beginning: if the test returns true then the compiler knows that the whole expression will be true and skips evaluating the expressions that throw.

Upvotes: 6

Related Questions