views:

250

answers:

11

Hi, this is a general question regarding method block structures. Does anyone have an opinion on what is a better way to design methods given the 2 alternatives below?

private void Method1()
{
    if (!A)
    {
        return;
    }

    if (!B)
    {
        return;
    }

    if (!C)
    {
        return;
    }

    // DO WORK.......

    return;
}

private void Method2()
{
    if (A)
    {
        if (B)
        {
            if (C)
            {
                // DO WORK.......

                return;
            }
        }
    }

    return;
}
+10  A: 

I prefer method 1, the "early exit" approach. In my opinion, it's more clear. And I really try to avoid lots of nested 'if' statements.

Also, you can't return 'null' in a void method :)

Noon Silk
yeah i just saw that hehe
Grant
You can write triangle code and just call a helper function if you find yourself nesting too deeply. My rule of thumb is if you do 6 in one function, your function is too complex. YMMV.
jeffamaphone
Kleinux
+5  A: 

Personal Preference.

I think I would do it more like this:

if(A && B && C) {
   // Do Work...
   return;
}
Ryan Cook
Noon Silk
silky: true, but he has "// Do Work" in a specific place, plus I think the spirit of the question is more about style and not the specific problem.
Ryan Cook
+2  A: 

The first method is debatable - personal preference to have multiple conditions or a single OR.

However the second method is bad, bad, bad. Multiple levels of indentation in a single method is just asking for bugs - maintainability and readability go way down. This is well-known, widely written about and documented.

Rex M
What sort of bugs?
jeffamaphone
what would you do instead if the first method is debatable?
Grant
Agree! Nested blocks are bad, as following the structure and logic is tough.
Tom Moseley
I disagree that it is difficult or "less readable." I've worked at (C++) shops where both are the required style and I find it only takes about two weeks to get used to the style change. In general triangle code can be just as readable and maintainable if you apply it consistently and for the right reasons. But I don't think any of those reasons are applicable to C#.
jeffamaphone
@jeffamaphone Since this question is exclusively about c#, for us morons who shouldn't try to mentally keep track of more than a few indentation levels at a time, it's best to play it safe.
Rex M
Sorry to disagree, Jeff. "triangle" code rapidly turns into "mountain range" code, as each if gets its assorments of else if and else clauses, making figuring out under which condition a particular piece of code executes impossible, in some cases. Better to make the conditions small, preferably refactored into separate methods.
Tom Moseley
@Rex M: That's not really a useful statement. I don't think anyone here is stupid, and I think the question, as asked, transcends limiting the domain of discourse to just C#. After all, you linked to a language-agnostic book. I think most people will agree there is a time and a place for constructs of all sorts and there is no simple answer that says #1 or #2 100% of the time. We must compare and contrast when each is and is not useful.
jeffamaphone
@Tom: I agree, if you indent too much, refactor. There really shouldn't be any else clauses (other then to maybe prepare to return or throw an error).
jeffamaphone
+1  A: 

You're trading tabs for return statements. You make the call.

Personally I like #2 for C++ where you're dealing with auto-releasing objects (e.g. CComPtr, AutoPtr, etc) but there is no need for that in C#. I would tend to use the first example for C# since you have garbage collection and you can worry less about making sure you cleanup everything on early exits.

jeffamaphone
A: 

Well, first, a void method can't return a value, but assuming you're returning an int?.

private int? method3 ()
{
  int? retVal;
  if (A && B && C)
  {
    // do work
    retVal = x;
  }
  return retVal;
}
Tom Moseley
You can use the return keyword in void methods to return immediately.
Mike Daniels
I know that. In a void method, you cannot return a value. This question was originally posed with a value being returned.
Tom Moseley
A: 

What about:

if(A && B && C)
{
     //Do work
}

return;

or

if(!A || !B || !C)
{
     return;
}

    //Do work
phsr
Why wrap your entire routine inside an extra indentation level when you can abort early and forget about that condition?
Rex M
Sometimes what you really want is: GetA(), if (!A) { return}, GetB(), etc... so you can't just check the existence. There are implied function calls between the if-statements. I think. Otherwise, yeah, this discussion is moot and your suggestion is the best solution.
jeffamaphone
Because its better than indenting three time. ;-)I never said I'd do it that way, I was just giving a suggestion.
phsr
+1  A: 

I will step out on a limb and say that either is probably a code smell. I don't think one should dogmatically adhere to the old "only one exit" philosophy of coding, but lots of exit points in a method can be just as much of a maintenance nightmare as deeply nested IF statements.

Refactoring lights should be going off all over the place when looking at either of those.

Josh
Lots of exit points are fine, in a preamble setting. Here we're making sure we have enough info to do whatever it is we're doing. Once we get past initialization I agree: multiple returns are bad news.
jeffamaphone
@jeffamaphone: I guess I tend to think of initialization more in terms of argument checking. The control would be broken by throwing an exception, but I understand what you are saying. I have some methods that do 4 or 5 checks that could result in an exception being thrown, and in that case trying to nest those would suck!
Josh
A: 

Put me down for #1.

John Lockwood
+1  A: 

I tend to try and always go with something that looks fairly easy to read:

private void Method1()
{
    if (!A ||
        !B ||
        !C)
    {
        return;
    }
    else if(D)
    {
        return y;
    }

    // DO WORK...

    return X;
}

but as others have mentioned it's entirely personal pref unless your workplace specifies one standard above others.

DerX
+1  A: 

Pre-condition logic can be factored out into a separate method:

private Boolean CanContinue()
{
  return A && B && C;
}

private void Method1()
{
  if (CanContinue())
  {
    // Do work...
  }
}

This has several advantages:

  • The pre-condition logic is a potential candidate for unit testing.
  • The logic can be re-used.
  • When the logic is complex, I think it's much easier to understand and maintain if it's encapsulated in its own method.
Chris R. Timmons
+2  A: 

A method which starts with three if statements before any useful work can be done has a distinctively foul code smell.

If your method begins with a string of if statements, stop for a moment, re-factor, then proceed.

Perhaps a switch statement would be a better candidate than a string of if statements.

Chris Ballance