6. using control structures, conditional statements and loops
1. Control Flow, Conditional
Statements and Loops
Correctly Organizing the Control Flow Logic
Nikolay Kostov
Telerik Software Academy
academy.telerik.com
Senior Software Developer
andTechnicalTrainer
http://Nikolay.IT
2. Table of Contents
Organizing Straight-line Code
Using Conditional Statements
Using Loops
Other Control Flow Structures
2
4. Straight-Line Code
When statements’ order matters
Make dependencies obvious
Name methods according to dependencies
Use method parameters
Document the control flow if needed
4
data = GetData();
groupedData = GroupData(data);
PrintGroupedData(groupedData);
GetData();
GroupData();
Print();
5. Straight-Line Code (2)
When statements’ order does not matter
Make code read from top to bottom like a
newspaper
Group related statements together
Make clear boundaries for
dependencies
Use blank lines to separate dependencies
User separate method
5
8. Straight-Line Code – Summary
The most important thing to consider when
organizing straight-line code is
Ordering dependencies
Dependencies should be made obvious
Through the use of good routine names,
parameter lists and comments
If code doesn’t have order dependencies
Keep related statements together
8
10. Using Conditional Statements
Always use { and } for the conditional
statements body, even when it is a single line:
Why omitting the brackets could be harmful?
This is misleading code + misleading formatting
10
if (condition)
{
DoSometing();
}
if (condition)
DoSomething();
DoAnotherThing();
DoDifferentThing();
11. Using Conditional Statements (2)
Always put the normal (expected) condition
first after the if clause
Start from most common cases first, then go
to the unusual ones
11
var response = GetHttpWebResponse();
if (response.Code == Code.OK)
{
// ...
}
else
{
if (response.Code == Code.NotFound)
{
// ...
}
}
var response = GetHttpWebResponse();
if (response.Code == Code.NotFound)
{
// ...
}
else
{
if (response.Code == Code.OK)
{
// ...
}
}
12. Using Conditional Statements (3)
Avoid comparing to true or false:
Always consider the else case
If needed, document why the else isn’t necessary
12
if (HasErrors)
{
...
}
if (HasErrors == true)
{
...
}
if (parserState != States.Finished)
{
// …
}
else
{
// Ignore all content once the pareser has finished
}
13. Using Conditional Statements (4)
Avoid double negation
Write if clause with a meaningful statement
Use meaningful boolean expressions, which
read like a sentence
13
if (HasErrors)
{
DoSometing();
}
if (!HasNoError)
{
DoSomething();
}
if (HasErrors)
{
DoSometing();
}
if (!HasError)
;
else
{
DoSometing();
}
14. Using Conditional Statements (5)
Be aware of copy/paste problems in if-else
bodies
14
Person p = null;
if (SomeCondition)
{
p = GetSomePerson();
}
else
{
p = GetOtherPerson();
}
p.SendMail();
p.SendSms();
if (SomeCondition)
{
var p = GetSomePerson();
p.SendMail();
p.SendSms();
}
else
{
var p = GetOtherPerson();
p.SendMail();
p.SendSms();
}
15. Use Simple Conditions
Do not use complex if conditions
You can always simplify them by introducing
boolean variables or boolean methods
Incorrect example:
Complex boolean expressions can be harmful
How you will find the problem if you get
IndexOutOfRangeException?
15
if (x > 0 && y > 0 && x < Width-1 && y < Height-1 &&
matrix[x, y] == 0 && matrix[x-1, y] == 0 &&
matrix[x+1, y] == 0 && matrix[x, y-1] == 0 &&
matrix[x, y+1] == 0 && !visited[x, y]) …
16. Simplifying Boolean Conditions
The last example can be easily refactored into
self-documenting code:
16
bool inRange = x > 0 && y > 0 && x < Width-1 && y < Height-1;
if (inRange)
{
bool emptyCellAndNeighbours =
matrix[x, y] == 0 && matrix[x-1, y] == 0 &&
matrix[x+1, y] == 0 && matrix[x, y-1] == 0 &&
matrix[x, y+1] == 0;
if (emptyCellAndNeighbours && !visited[x, y]) …
}
Now the code is:
Easy to read – the logic of the condition is clear
Easy to debug – breakpoint can be put at the if
18. Simplifying Boolean Conditions (3)
Now the code:
Models the real scenario
Stays close to the problem domain
18
bool AreNeighbourCellsEmpty()
{
…
}
bool ShouldVisitCurrentCell()
{
return
IsCurrentCellInRange() &&
CurrentCell.IsEmpty() &&
AreNeighbourCellsEmpty() &&
!IsCurrentCellVisited()
}
}
19. Use DecisionTables
Sometimes a decision table can be used for
simplicity
19
var table = new Hashtable();
table.Add("A", new AWorker());
table.Add("B", new BWorker());
table.Add("C", new CWorker());
string key = GetWorkerKey();
var worker = table[key];
if (worker != null)
{
...
worker.Work();
...
}
20. Positive Boolean Expressions
Starting with a positive expression improves
the readability
Use De Morgan’s laws for negative checks
20
if (IsValid)
{
DoSometing();
}
else
{
DoSometingElse();
}
if (!IsValid)
{
DoSometingElse();
}
else
{
DoSomething();
}
if (!(IsValid && IsVisible))
if (!IsValid || !IsVisible)
21. Use Parentheses for Simplification
Avoid complex boolean conditions without
parenthesis
Using parenthesis helps readability as well as
ensure correctness
Too many parenthesis have to be avoided as
well
Consider separate Boolean methods or
variables in those cases
21
if (a < b && b < c || c == d)
if (( a < b && b < c ) || c == d)
22. Boolean Expression Evaluation
Most languages evaluate from left to right
Stop evaluation as soon as some of the boolean
operands is satisfied
Useful when checking for null
There are languages that does not follow this
“short-circuit” rule
22
if (FalseCondition && OtherCondition)
if (TrueCondition || OtherCondition) = true
= false
if (list != null && list.Count > 0) …
23. Numeric Expressions as Operands
Write numeric boolean expressions as they are
presented on a number line
Contained in an interval
Outside of an interval
23
if (x > a && b > x)
if (a < x && x < b) a bx
if (a > x || x > b)
if (x < a || b < x) a bx x
24. Avoid Deep Nesting of Blocks
Deep nesting of conditional statements and
loops makes the code unclear
More than 2-3 levels is too deep
Deeply nested code is complex and hard to read
and understand
Usually you can extract portions of the code in
separate methods
This simplifies the logic of the code
Using good method name makes the code self-
documenting
24
25. if (maxElem != Int32.MaxValue)
{
if (arr[i] < arr[i + 1])
{
if (arr[i + 1] < arr[i + 2])
{
if (arr[i + 2] < arr[i + 3])
{
maxElem = arr[i + 3];
}
else
{
maxElem = arr[i + 2];
}
}
else
{
if (arr[i + 1] < arr[i + 3])
{
maxElem = arr[i + 3];
}
else
{
maxElem = arr[i + 1];
}
}
}
Deep Nesting – Example
25
(continues on the next slide)
27. Avoiding Deep Nesting – Example
27
private static int Max(int i, int j)
{
if (i < j)
{
return j;
}
else
{
return i;
}
}
private static int Max(int i, int j, int k)
{
if (i < j)
{
int maxElem = Max(j, k);
return maxElem;
}
else
{
int maxElem = Max(i, k);
return maxElem;
}
} (continues on the next slide)
28. Avoiding Deep Nesting –
Example (2)
28
private static int FindMax(int[] arr, int i)
{
if (arr[i] < arr[i + 1])
{
int maxElem = Max(arr[i + 1], arr[i + 2], arr[i + 3]);
return maxElem;
}
else
{
int maxElem = Max(arr[i], arr[i + 2], arr[i + 3]);
return maxElem;
}
}
if (maxElem != Int32.MaxValue) {
maxElem = FindMax(arr, i);
}
29. Using Case Statement
Choose the most effective ordering of cases
Put the normal (usual) case first
Order cases by frequency
Put the most unusual (exceptional) case last
Order cases alphabetically or numerically
Keep the actions of each case simple
Extract complex logic in separate methods
Use the default clause in a case statement or the
last else in a chain of if-else to trap errors
29
30. Incorrect Case Statement
30
void ProcessNextChar(char ch)
{
switch (parseState)
{
case InTag:
if (ch == ">")
{
Console.WriteLine("Found tag: {0}", tag);
text = "";
parseState = ParseState.OutOfTag;
}
else
{
tag = tag + ch;
}
break;
case OutOfTag:
…
}
}
31. Improved Case Statement
31
void ProcessNextChar(char ch)
{
switch (parseState)
{
case InTag:
ProcessCharacterInTag(ch);
break;
case OutOfTag:
ProcessCharacterOutOfTag(ch);
break;
default:
throw new InvalidOperationException(
"Invalid parse state: " + parseState);
}
}
32. Case – Best Practices
Avoid using fallthroughs
When you do use them, document them well
32
switch (c)
{
case 1:
case 2:
DoSomething();
// FALLTHROUGH
case 17:
DoSomethingElse();
break;
case 5:
case 43:
DoOtherThings();
break;
}
33. Case – Best Practices(2)
Overlapping control structures is evil:
33
switch (inputVar)
{
case 'A': if (test)
{
// statement 1
// statement 2
case 'B': // statement 3
// statement 4
...
}
...
break;
}
This code will not compile in C# but may compile
in other languages
34. Control Statements – Summary
For simple if-else-s, pay attention to the order
of the if and else clauses
Make sure the nominal case is clear
For if-then-else chains and case statements,
choose the most readable order
Optimize boolean statements to improve
readability
Use the default clause in a case statement or
the last else in a chain of if-s to trap errors
34
36. Using Loops
Choosing the correct type of loop:
Use for loop to repeat some block of code a certain
number of times
Use foreach loop to process each element of an
array or a collection
Use while / do-while loop when you don't know
how many times a block should be repeated
Avoid deep nesting of loops
You can extract the loop body in a new method
36
37. Loops: Best Practices
Keep loops simple
This helps readers of your code
Treat the inside of the loop as it were a routine
Don’t make the reader look inside the loop to
understand the loop control
Think of a loop as a black box:
37
while (!inputFile.EndOfFile() && !hasErrors)
{
}
(black box code)
38. Loops: Best Practices (2)
Keep loop’s housekeeping at the start or at the
end of the loop block
Use meaningful variable names to make loops
readable
38
while (index < 10)
{
...
...
index += 2;
}
while (index < 10)
{
...
index += 2;
...
}
for(i=2000, i<2011, i++)
{
for(j=1, j<=12, j++)
...
}
for (year=2000, year<2011, year++)
{
for(month=1, month<=12, month++)
...
}
39. Loops: Best Practices (3)
Avoid empty loops
Be aware of your language (loop) semantics
C# – access to modified closure
39
do
{
inputChar = Console.Read();
}
while (inputChar != 'n');
while ((inputChar = Console.Read()) != 'n')
{
}
40. Loops:Tips on for-Loop
Don’t explicitly change the index value to force
the loop to stop
Use while-loop with break instead
Put only the controlling statements in the loop
header
40
for (i = 0, sum = 0;
i < length;
sum += arr[i], i++)
{
;
}
sum = 0;
for (i = 0; i < length; i++)
{
sum += arr[i];
}
41. Loops:Tips on for-Loop(2)
Avoid code that depends on the loop index’s
final value
41
for (i = 0; i < length; i++)
{
if (array[i].id == key)
{
break;
}
}
// Lots of code
...
return (i >= length);
bool found = false;
for (i = 0; i < length; i++)
{
if (array[i].id == key)
{
found = true;
break;
}
}
// Lots of code
...
return found;
42. Loops: break and continue
Use continue for tests at the top of a loop to
avoid nested if-s
Avoid loops with lots of brake-s scattered
trough it
Use break and continue only with caution
42
43. How Long Should a Loop Be?
Try to make the loops short enough to view it
all at once (one screen)
Use methods to shorten the loop body
Make long loops especially clear
Avoid deep nesting
in loops
43
45. The return Statement
Use return when it enhances readability
Use return to avoid deep nesting
Avoid multiple return-s in long methods
45
void ParseString(string str)
{
if (string != null)
{
// Lots of code
}
}
void ParseString(string str)
{
if (string == null)
{
return;
}
// Lots of code
}
46. Recursion
Useful when you want to walk a tree / graph-
like structures
Be aware of infinite recursion or indirect
recursion
Recursion example:
46
void PrintWindowsRecursive(Window w)
{
w.Print()
foreach(childWindow in w.ChildWindows)
{
PrintWindowsRecursive(childWindow);
}
}
47. RecursionTips
Ensure that recursion has end
Verify that recursion is not very high-cost
Check the occupied system resources
You can always use stack classes and iteration
Don’t use recursion when there is better linear
(iteration based) solution, e.g.
Factorials
Fibonacci numbers
Some languages optimize tail-call recursions
47
48. GOTO
Avoid goto-s, because they have a tendency
to introduce spaghetti code
“A Case Against the
GOTO Statement”
by Edsger Dijkstra
Use goto-s as a last resort
If they make the code
more maintainable
C# supports goto with
labels, but avoid it!
48
49. форумпрограмиране,форум уеб дизайн
курсовеи уроци по програмиране,уеб дизайн – безплатно
програмиранеза деца – безплатни курсове и уроци
безплатенSEO курс -оптимизация за търсачки
уроципо уеб дизайн, HTML,CSS, JavaScript,Photoshop
уроципо програмиранеи уеб дизайн за ученици
ASP.NETMVCкурс – HTML,SQL,C#,.NET,ASP.NETMVC
безплатенкурс"Разработка на софтуер в cloud среда"
BGCoder -онлайн състезателна система -online judge
курсовеи уроци по програмиране,книги – безплатно отНаков
безплатенкурс"Качествен програменкод"
алгоакадемия – състезателно програмиране,състезания
ASP.NETкурс -уеб програмиране,бази данни, C#,.NET,ASP.NET
курсовеи уроци по програмиране– Телерик академия
курсмобилни приложения с iPhone, Android,WP7,PhoneGap
freeC#book, безплатна книга C#,книга Java,книга C#
Дончо Минков -сайт за програмиране
Николай Костов -блог за програмиране
C#курс,програмиране,безплатно
Control Flow, Conditional
Statements and Loops
http://academy.telerik.com
50. Homework (1)
1. Refactor the following class using best practices
for organizing straight-line code:
50
public void Cook()
public class Chef
{
private Bowl GetBowl()
{
//...
}
private Carrot GetCarrot()
{
//...
}
private void Cut(Vegetable potato)
{
//...
}
// continue on the next slide
52. Homework (3)
2. Refactor the following if statements:
52
Potato potato;
//...
if (potato != null)
if(!potato.HasNotBeenPeeled && !potato.IsRotten)
Cook(potato);
if (x >= MIN_X && (x =< MAX_X && ((MAX_Y >= y &&
MIN_Y <= y) && !shouldNotVisitCell)))
{
VisitCell();
}
53. Homework (4)
3. Refactor the following loop:
53
int i=0;
for (i = 0; i < 100;)
{
if (i % 10 == 0)
{
Console.WriteLine(array[i]);
if ( array[i] == expectedValue )
{
i = 666;
}
i++;
}
else
{
Console.WriteLine(array[i]);
i++;
}
}
// More code here
if (i == 666)
{
Console.WriteLine("Value Found");
}
54. FreeTrainings @Telerik Academy
C# Programming @Telerik Academy
csharpfundamentals.telerik.com
Telerik Software Academy
academy.telerik.com
Telerik Academy @ Facebook
facebook.com/TelerikAcademy
Telerik Software Academy Forums
forums.academy.telerik.com
Notas del editor
string[] names = { "Fred", "Barney", "Betty", "Wilma" };foreach (string name in names){this.button.Click += delegate {MessageBox.Show("Hello, " + name); };}