views:

269

answers:

8

I've responded to threads here (or at least commented) with answers containing code like this, but I'm wondering if it's good or bad form to write a series of if branches with one (or more) of the branches doing nothing in them, generally to eliminate checking for null in every branch.

An example (C# code):

if (str == null) { /* Do nothing */ }
else if (str == "SomeSpecialValue")
{
    // ...
}
else if (str.Length > 1)
{
    // ...
}

instead of:

if (str != null && str == "SomeSpecialValue")
{
    // ...
}
else if (str != null && str.Length > 1)
{
    // ...
}

And, of course, this is just an example, as I tend to use these with larger and more complex classes. And in most of these cases, a null value would indicate to do nothing.

For me, this reduces the complication of my code and makes sense when I see it. So, is this good or bad form (a code smell, even)?

+18  A: 

I prefer doing it like this-

if (str != null) 
{
 if (str == "[NULL]")
 {
    // ...
 }
 else if (str.Length > 1)
 {
    // ...
 }
}  

I think you can always "reword" an if with an empty body into it's negation with a body, and that it looks better and makes more sense.

Oren A
I think I have an aversion to too much indentation. It also saves me a bit of vertical space, and in C#, at least, I'm sure the compiler will handle it well.
palswim
The compiler will handle it well, but IMHO what you're saying has an awkward logic. Why say "if... then do nothing", when you can say "if not.. than do.." About the identation, you can reduce the number of spaces a tab takes, which may help at least a bit.
Oren A
Not wanting to properly format code is not a good argument against writing code properly.
nearlymonolith
@palswim Anthony Morelli is right. And the indentation is there for the benefit of human readers--not the compiler.
@user359996: Needing less indentation and vertical space are precisely for the benefit of the readers in this case.
palswim
@palswim Sacrificing readability for horizontal space does not benefit the reader (of course, many would agree what you're doing *doesn't* sacrifice readability--then again, I think I laid down a pretty clear argument that it does, in my answer).
this is *a lot* more readable -
pm100
+1 Doing things the obvious, logical and idiomatic has the best chance of resulting in obvious, logical and idiomatic code.
Jon Hanna
+1  A: 

Your first example is perfectly readable to me -- doesn't smell at all.

John at CashCommons
+3  A: 

In this particular case I will return early and it makes code easier to read

if (string.IsNullOrEmpty(str)) { return; }  

I like to put an explicit return statement.

Pratik
That only works if the function does nothing else but the `if...else` block.
palswim
@Pratik This really depends on the semantics of the problem. Very rarely does a null argument mean "do nothing". All too often it means someone has made a mistake, and by silently returning, you're just aggravating the problem. If it's not life-support or the military, fail-fast is often a good idea.
I completely agree with you and I always do fail fast. I am only replying to this particular question where the use for whatever reason want's to return. This often happens in batch processing where instead of failing you just skip and possibly log what entity was skipped.
Pratik
+2  A: 

It all depends on context. If putting an empty if statement makes the code more readable, then go for that.

muad_dib
Yes, but always document it when you do that. Or, if you're like me and try to make code that doesn't need comments and you're not working in a language that forces everything to be in a class, call a method named Nothing().
Loren Pechtel
I'm used to working with Python, which has 'pass' included in it (a statement to do nothing).
muad_dib
+1  A: 

It's readable, whether it is good or bad depends upon what you are trying to achieve - generally long nested "goes-on-forever" type if statements are bad. Don't forget about static string methods baked into the framework: string.IsNullOrEmpty() and string.IsNullOrWhiteSpace().

Your if (str == null) { /* Do nothing */ } line is unusual, but does have one positive point: it is letting other developers know up front that you are deliberately doing nothing for that case, with your long if/else if structure your intentions could become unclear if you changed it to

if (str != null) 
{ 
    /* carry on with the rest of the tests */
}
slugster
+1  A: 

It is indeed good to avoid the following, because it needlessly re-checks one of the conditions (the fact that the compiler will optimize this away is beside the point--it potentially makes more work for folks trying to read your code):

if (str != null && str == "SomeSpecialValue")
{
    // ...
}
else if (str != null && str.Length > 1)
{
    // ...
}

But it's also rather bizarre to do what you suggested, below:

if (str == null) { /* Do nothing */ }
else if (str == "SomeSpecialValue")
{
    // ...
}
else if (str.Length > 1)
{
    // ...
}

I say this is bizarre because it obfuscates your intent and defies the reader's expectations. If you check for a condition, people expect you to do something if it is satisfied--but you're not. This is because your intent is not to actually process the null condition, but rather to avoid a null pointer when you check the two conditions you're actually interested in. In effect, rather than having two conceptual states to handle, with a sanity provision (non-null input), it reads instead like you have three conceptual states to handle. The fact that, computationally, you could say there are three such states is beside the point--it's less clear.

The usual case approach in this sort of situation is as Oren A suggested--check for the null, and then check the other conditions within the result block:

if (str != null) 
{
 if (str == "SomeSpecialValue")
 {
    // ...
 }
 else if (str.Length > 1)
 {
    // ...
 }
}

This is little more than a matter of readability-enhancing style, as opposed to an issue of code smell.

EDIT: However, if you're set on the do-nothing condition, I do very much like that you included a "do nothing" comment. Otherwise, folks might think you simply forgot to complete the code.

See edit - I took that (the "[NULL]" string) out because people were getting hung up on that.
palswim
@palswim Okay. But note that wasn't really the main point of my answer. (But at the same time, that code came from somewhere--if it's production code, it's almost certainly a bad thing).
@user359996: Nope; I didn't want to copy and paste my production code. I was trying to think of an example on the spot.
palswim
I think it would be fair to say that my brain does have a code smell. Any time I try to provide an example on this site, someone points out a bad practice in the sample, usually about the example data I've chosen.
palswim
+3  A: 

I would normally put a return or something like that in the first if:

void Foo()
{
  if (str == null) { return; }
  if (str == "SomeSpecialValue")
  {
      // ...
  }
  else if (str.Length > 1)
  {
      // ...
  }
}

If you can't do this, because the function does something else after the if/else, I'd say it's time to refactor, and split the if/else part out into a separate function, from which you can return early.

jalf
I don't like your indentation style, but this piece of code is the best way, IMHO, to solve the OP's problem. +1
rmeador
@rmeador: Interesting; which indentation style do you like?
palswim
it's not really "my" indentation style, I just tried to stick with the OP's. :)
jalf
+2  A: 

Yes it is a code smell.

One indication is that you thought to ask this question.

Another indication is that the code looks incomplete- as if something should belong there. It may be readable sure, but it feels off.

When reading that code, an outsider has to stop for a second and use brainpower to determine if the code is valid/complete/correct/as intended/adjective.

user359996 hit the nail on the head:

I say this is bizarre because it obfuscates your intent and defies the reader's expectations.

instanceofTom
I don't think that "thinking to ask the question" is necessarily a bad indicator. Almost none of the code I see, even classic examples of 'good code', looks good to me. Almost every design pattern looks like a code smell to me, for example. Almost every example of procedural code makes me "stop for a second and use brainpower" to figure out what was intended.
Ken
@Ken In general "thinking to ask" may not be a sign of a code smell, but in this situation Id say it was. If no code looks good to you, you either are reading the wrong source, or for some reason just don't like the look of code. (Many) Design patterns are not code smells... exposures of weakness in a language maybe. For the above example- there are better things to get 'stopped on' and expend brain power for than simple code that looks incomplete. I am sorry procedural code causes you trouble.
instanceofTom
instanceofTom: I am sorry a pair of braces with no statements causes you trouble.
Ken
@Ken, insert witty response **here**
instanceofTom