C#: Nested conditionals vs continue statement
In using ReSharper recently, it is suggesting I reduce nesting in certain places by inverting if
conditions and using the continue
statements.
nested conditionals:
foreach(....)
{
if(SomeCondition)
{
//do some things
if(SomeOtherNestedCondition)
{
//do some further things
}
}
}
continue statements:
foreach(....)
{
if(!SomeCondition) continue;
//do some things
if(!SomeOtherNestedCondition) continue;
//do some further things
}
I understand some of the logic of why you'd want to reduce nesting for performance and memory issues as well as how the two snippets equate to each other, however from my development background, the before example is easier to follow when reading the 开发者_开发问答code.
Which approach do you prefer and why? Do you use continue
over nested ifs in your everyday code?
As a rule I have found it best to always start statement blocks with any conditions that will except out as it reduces complexity, but more importantly throws out non-compatible circumstances before they are stepped any further which can increase code and memory performance. This also ensures safety of your conditions over a duration through maintenance, that it's less likely to have invalid scenarios passed into code they don't belong in.
Plus I think the second of the two is more readable personally because you don't have the scope layers confusing what's available, it's easy to create a variable in one layer later down the road and not realize it's unavailable in another layer, or having to manage them to be modified appropriately etc.
This isn't just continue in loops, but this also refers to conditions of methods should return; instead of having a method start
if (valid)
{
do stuff;
}
it should always start
if (notValid)
{
return;
}
There should be no significant performance different, this is all about readability. Personally I think the latter is easier to read. Less nesting, easier to read.
Short answer:
I tend to use indentation such that it implies that some real decision logic takes place, ie. logic where more than one possible execution path is expected.
Longer answer:
I usually prefer indented blocks to preceding "breaking" statements (continue
, break
, return
, or throw
).
Reason: In my opinion, breaking statements generally make code harder to read. If you indent code, it's easy to find the location where some decision logic happens: that'll be the first line further up the code that is indented less. If you use breaking statements with an inverted condition instead, you have to do more work to understand how code branches and under which circumstances certain code is skipped.
There is one notable exception for me, namely validating arguments at the beginning of a method. I format this code as follows:
if (thisArgument == null) throw new NullArgumentException("thisArgument");
if (thatArgument < 0) throw new ArgumentOutOfRangeException("thatArgument");
...
Reason: Since I don't expect these exceptions to be actually thrown (that is, I expect the method to be invoked correctly; the calling function is responsible to detect invalid input, IMO), I don't want to indent all the rest of the code for something that shouldn't happen.
With the continue
style code; how would you handle a third operation that isn't dependent on SomeOtherNestedCondition, this makes the order of the code important which IMO makes it less maintainable.
For example:
foreach(....)
{
if(!SomeCondition) continue;
//do some things
if(!SomeOtherNestedCondition) continue;
//do some further things
if(!SomeSecondNonNestedCondition) continue;
// do more stuff
}
What happens when SomeOtherNestedCondition causes the continue;
to happen, but SomeSecondNonNestedCondition should still execute?
I would refactor out each bit of "code" and use nested if()
s to call each refactored method, and I'd keep the nested structure.
Simple answer. Which ever one is easier to read and understand.
Use a combination of the two. I use if(condition) continue;
and if(condition) return;
at the top of the loop to validate the current state, and nested if
statements further down to control the flow of whatever I'm trying to accomplish.
There is no difference in terms of memory or performance. The underlying IL is just a selection of branch/jump instructions so it doesn't really matter whether they jump back to the top of the loop or to the "else" statement.
You should pick whichever is easier to read. I personally prefer to avoid 'continue', but if you have many levels of nesting then your second example can be easier to follow.
Using continue
s makes the bulk of the code on the level of regular procedural code.
Otherwise, if there are five checks, you'll indent the "meat" of the method 10 or 20 chars, depending on the indent size, and those are 10/20 chars that you'll have to scroll to see the longer lines.
Original continue
meaning: The code won't be processed while any condition is false. Here is an example of the situation:
class Program
{
static bool SomeCondition = false;
static bool SomeOtherNestedCondition = false;
static void Main(string[] args)
{
for (int i = 0; i < 2; i++)
{
if (SomeCondition)
{
//do some things
if (SomeOtherNestedCondition)
{
//do some further things
}
}
Console.WriteLine("This text appeared from the first loop.");
}
for (int i = 0; i < 2; i++)
{
if (!SomeCondition) continue;
//do some things
if (!SomeOtherNestedCondition) continue;
//do some further things
Console.WriteLine("This text appeared from the second loop.");
}
Console.ReadLine();
}
}
And output will be:
however from my development background, the before example is easier to follow when reading the code.
Well - I think, that's your personal opinion. I - for example - try to avoid such nesting as it makes the code harder to read in my opinion.
If you like the "before" version more than the "after" one, use it.
Just configure ReSharper so it suggests what you really want it to.
Always keep in mind: ReSharper is a quite "dumb" refactoring tool - it cannot replace the developer. It can only help him by doing some otherwise stupid copy&paste work.
Some refactorings done by ReSharper result even in situations in whcih ReSharper suggests the opposite refactoring.
Therefore, don't see ReSharper's suggestions as best practices but as possibilities you could do.
BTW: You should be driven by performance considerations here - I would be surprised if there are really notable differences in performance.
精彩评论