1

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?

PiousVenom
  • 6,780
  • 8
  • 43
  • 83
  • Almost all cases of `NullReferenceException` are the same. Please see "[What is a NullReferenceException in .NET?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net)" for some hints. – John Saunders May 15 '14 at 19:32
  • @JohnSaunders: My question is not why is it throwing the exception or what the exception is, it was "Why are all conditions not evaluated before throwing the exception. – PiousVenom May 15 '14 at 19:35
  • Why _would_ they all be evaluated? There's no magic in .NET that ever says "go evaluate all expressions in this context and only _then_ go do something". – John Saunders May 15 '14 at 19:37
  • @JohnSaunders: When I wrote this question, 2 years ago, I didn't know about short-circuit evaluation. I thought that since I had made the conditionals as an `or`, that it would evaluate all of them. I now do know better, but my point still stands, that this question is NOT a duplicate of the question you linked to and subsequently closed my question. – PiousVenom May 15 '14 at 19:39
  • Yeah, it _is_ a duplicate. It's "failure to check for null first". And short-circuit evaluation has nothing to do with this. If you use `var a = new Something(); if(a.Prop1.Prop2 == null | a.Prop1.Prop3 == null)`, then the compiler will _try_ to evaluate both expressions, but will fail because `Prop1` is null. – John Saunders May 15 '14 at 19:44
  • @JohnSaunders: No, this is not a duplicate. I'm not asking "What is a NullReferenceException?". I was asking why all conditionals aren't evaluated. It could very well have been any other exception that I was getting, and I would have asked the same question. – PiousVenom May 15 '14 at 19:46
  • What you're not seeing is that conditionals are expressions. The syntax of `if` is: `if (booleanExpression) statement;`. The `booleanExpression` will be evaluated. If that evaluation throws an exception, then that's just life. It doesn't matter if the `booleanExpression` is composed of other expressions - if expression evaluation throws an exception, that's the end of expression evaluation. – John Saunders May 15 '14 at 19:48
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/52791/discussion-between-mycodesucks-and-john-saunders) – PiousVenom May 15 '14 at 19:49

3 Answers3

6

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.

Jon
  • 396,160
  • 71
  • 697
  • 768
  • Thanks, Jon. You've been most helpful with your answer. – PiousVenom Nov 01 '12 at 14:45
  • @Prayos: You 're welcome. Keep in mind that short-circuiting also applies to `&&` (but there it triggers if the left-hand-side is `false`). – Jon Nov 01 '12 at 14:47
  • For my own sanity, is there any kind of check that will evaluate all of the conditions set forth before it fails? – PiousVenom Nov 01 '12 at 14:54
  • @Prayos: No, and I don't think it's a good idea to try and go your own way here by trying to devise some trick. It goes against *decades* of programming practice. – Jon Nov 01 '12 at 14:56
  • No worries about me attempting to do that. I was just curious if it existed. Thanks a lot. – PiousVenom Nov 01 '12 at 15:03
2

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 ...
}
Joe
  • 114,633
  • 27
  • 187
  • 321
  • I voted your answer down because in my question, I clearly stated that I know I can put the null check first, and it works. My question is "Why?" Not how do I fix it. – PiousVenom Nov 01 '12 at 14:38
  • I removed the downvote, as your edit actually supplies me with a better way of doing this. Thank you. – PiousVenom Nov 01 '12 at 14:39
  • @Prayos, I'm not sure I understand your question. Why would you expect it to continue doing anything when a statement that throws is executed? – Joe Nov 01 '12 at 14:43
  • I think that I expected it to check all conditionals before it actually failed. I always assumed that when you have multiple conditions in an if statement, it evaluates ALL of them. I now see that I was wrong. Also, I wanted to thank you for pointing out the flaw in my check. Your check is working quite nicely. – PiousVenom Nov 01 '12 at 14:45
  • @Prayos "I always assumed that when you have multiple conditions in an if statement, it evaluates ALL of them" - imagine one of the conditions calls a method that throws. How could it continue? – Joe Nov 01 '12 at 15:45
1

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.

John Saunders
  • 157,405
  • 24
  • 229
  • 388