Because of their limited use and appeal, labeled statements are only described in the narrow context of the goto and switch statements.. then braces are required to enclose the two state
Trang 1Statements and Exceptions
In C#, statements fall into one of three categories: labeled, declaration, and
embedded This is shown by the following EBNF definition: EBNF
Stmt = LabeledStmt | DeclStmt | EmbeddedStmt
Embedded statements include the full gamut of conditional, iterative, and transfer
state-ments that one would expect in any high-level language Additional constructs for mutual
exclusion and the use of resources are also provided
In this chapter, we first present one type of embedded statement called the block
statement Declaration statements and all other embedded statements are then discussed
with particular emphasis on the exception-handling mechanism of C# Because of their
limited use and appeal, labeled statements are only described in the narrow context of the
goto and switch statements
6.1 Block Statement
A block is an embedded statement that encloses zero or more statements within a pair of
Block = "{" Stmts? "}"
Each statement in a block is executed in sequence, but the block itself returns no value
Although a block is not required for only one statement, it is a useful practice to enclose Tip
a single statement within braces, as would become required if additional statements were
added For example, if a debugging Write is added to this statement:
if ( x > 0 )
x = temp;
107
Trang 2then braces are required to enclose the two statements as a single block:
A declaration statement declares and initializes one or more local variables or constants
of a given type In the case of a constant, the type must be preceded by the keywordconst and the initialization must be explicit Variables need not be explicitly initializedupon declaration since number, character, and reference-type variables are initialized bydefault to 0, ‘\0’, and null, respectively The complete EBNF definition for declarationstatements is given here:
LocalConstantDecl = "const" Type ConstantDecltorList
ConstantDecltor = Identifier "=" ConstantExpr
A variable declaration can appear anywhere in a block, and not just at the beginning as in
C The scope of the variable, however, ranges from its point of declaration to the end ofthe innermost block in which it is declared For example:
{
for (int n = 0; n < 8; n++) {// n is in the scope of the for
}
char c; // Declaration closer to its related code
}
Trang 3Finally, any variable that is used before declaration or is accessed outside its scope
generates a compilation error
6.3 Embedded Statements
Embedded statements in C# include many of the well-known constructs in C/C++ and
Java, such as block statements (described previously), expression statements, empty
state-ments, selection statestate-ments, and iteration statements A summary of these statements and
others in C# are listed in the following EBNF definition and described in the sections that
EmbeddedStmt = ExprStmt | EmptyStmt | Block | SelectionStmt
| IterationStmt | JumpStmt | TryStmt | CheckedStmt
| UncheckedStmt | LockStmt | UsingStmt | YieldStmt
6.3.1 Expression and Empty Statements
An expression statement is simply an expression with a semicolon at the end However,
only the following types of expressions can become statements:
■ Method invocations,
■ Object creation expressions (with new),
■ Assignment expressions containing = or op= operators, and
■ Expressions with ++ and operators (prefix or postfix)
The complete EBNF definition of an expression statement is given here: EBNFExprStmt = StmtExpr ";"
StmtExpr = InvocationExpr | ObjectCreationExpr | Assignment |
PostIncExpr | PostDecExpr | PreIncExpr | PreDecExpr
An empty statement, on the other hand, is simply represented by a semicolon. EBNFEmptyStmt = ";"
An empty statement is often used in conjunction with the for statement as illustrated
here:
for (i = 0; i < n && A[i]!=x; i++)
; // Empty statement
In this case, all the necessary comparisons and assignments that search for value x in array
A of size n are encapsulated as part of the control mechanism of the for statement itself
Trang 46.3.2 Selection Statements
Selection statements allow a program to choose one of a number of possible actions for
execution These selection statements include the if and switch statements
bool directionUp;
if (directionUp) { // If statement with an else part
if (++count > max) { // Nested if statement without an else partcount = min;
return true;
}} else {
if ( count < min) { // Nested if statement without an else partcount = max;
return true;
}}
return false;
Another variation of the if statement uses the else if clause to select one of many tives In the following example, this variation selects one of four alternatives based on thevalue of an operator character A similar example using the switch statement is presented
alterna-in the next subsection
Trang 5} else { // Default case
}
switch Statement
The switch statement is used to choose one of many alternative actions based on the value
SwitchStmt = "switch" "(" Expr ")" SwitchBlock
SwitchBlock = "{" SwitchSections? "}"
SwitchSection = SwitchLabels StmtList
SwitchLabel = ( "case" ConstantExpr ":" ) | ( "default" ":" )
A switch statement is executed as follows:
■ The switch expression is evaluated Only expressions that evaluate to an integral,
character, enumerated, or string type are permitted
■ If one of the case label constants is equal to the expression, control is transferred
to the list of statement(s) following the case label that is matched After execution
of the associated statement(s), a break statement must be used to reach the end of
the switch statement Unlike C/C++ and Java, control does not fall through to the
next case section unless a goto statement is used In fact, the traditional fall-through
strategy results in a compilation error in C#
■ If no case label is equal to the value of the expression, the default label (if any) is
case ‘+’: Console.WriteLine(" = {0}", int1 + int2); break;
case ‘-’: Console.WriteLine(" = {0}", int1 - int2); break;
case ‘x’: goto case ‘*’; // To obtain a fall through
case ‘*’: Console.WriteLine(" = {0}", int1 * int2); break;
case ‘/’: if ( int2 != 0 )
Console.WriteLine(" = {0}", int1 / int2);
elseConsole.WriteLine("Divide by zero");
break;
default: Console.WriteLine("Invalid operator: must be + - * x /");
break;
}
Trang 66.3.3 Iteration Statements
Iteration statements, or loops, allow a single statement or block to be executed
repeat-edly The loop condition is a boolean expression that determines when to terminate theloop C# provides four kinds of loops: while, do-while, for, and foreach statements
while Statement
The syntax of the while loop is:
EBNF
WhileStmt = "while" "(" BooleanExpr ")" EmbeddedStmt
EmbeddedStmt is executed zero or more times until the BooleanExpr evaluates to false.Example:
DoStmt = "do" EmbeddedStmt "while" "(" BooleanExpr ")" ";"
EmbeddedStmt is executed one or more times until the BooleanExpr evaluates tofalse
Example (giving the same output):
Trang 7for Statement
ForStmt = "for" "(" ForInitializer? ";" ForCondition? ";" ForIterator? ")"
EmbeddedStmt and is equivalent to the following statements:
ForeachStmt = "foreach" "(" Type Identifier "in" Expr ")" EmbeddedStmt
The foreach statement enumerates the elements of a given collection and executes the
embedded statement for each one The Type and Identifier declare a read-only
itera-tion variable to be used locally within the scope of the embedded statement During the
loop execution, this iteration variable represents a collection element A compilation error
Trang 8occurs if the variable is (1) modified via assignment or the ++ and operators or (2) passed
as a ref or out parameter
C# offers five kinds of jump statements that unconditionally transfer control in an
application: goto, continue, break, return, and exception handling (throw and try) ments Because of its importance, exception handling is discussed separately in the nextsection
state-goto and Labeled Statements
A labeled statement allows a statement to be preceded by an Identifier label Labels
are permitted within blocks only, and their scope includes any nested blocks
EBNF
LabeledStmt = Identifier ":" EmbeddedStmt
In C#, the name of an identifier label never conflicts with any other identifier for localvariables, fields, and so on Outside the normal sequence of execution, a labeled statement
is reached by using a goto statement within the same scope (or block) In general, the gotostatement transfers control to any statement that is marked by a label including a caselabel as defined here:
EBNF
ContinueStmt = "continue" ";"
Trang 9The break statement is used in labeled blocks, loops (while, do-while, for, or foreach),
and switch statements in order to transfer control out of the current context, that is, the
The return statement returns control to the caller of the current method and has one of
two forms: void (using return;) and non-void (using return Expr;) as shown here: EBNFReturnStmt = "return" Expr? ";"
Example:
using System;
class Factorial {
// non-void method must return a value
static int Process(int i) {
return i * Process(i-1); // recursion invocation
Trang 10elsereturn 1;
}public static void Main(string[] args) {
if (args.Length == 0) {Console.WriteLine("Usage: Factorial <n>");
return; // main is a void method that can use return
}int n = Int32.Parse(args[0]);
Console.WriteLine(n + "! = " + Process(n));
}}
In the case of the non-void return, the type of the Expr value must be compatible withthe return type specified by the method For example, if 1.0 is returned instead of 1 inthe previous example, then a compilation error is generated Therefore, the static methodInt32.Parse is used to convert the string args[0] to its integer equivalent
6.3.5 checked/unchecked Statements
The checked and unchecked statements control the context of overflow checking forintegral-type arithmetic operations and conversions These statements were covered inChapter 5
6.3.6 lock and using Statements
The lock statement delimits an embedded statement as a mutually exclusive, criticalsection for an object represented by the expression Expr
EBNF
LockStmt = "lock" "(" Expr ")" EmbeddedStmt
Because no implicit boxing is performed on Expr, the expression must be a reference type.Otherwise, a compile-time error is generated The lock mechanism itself is implementedwith a monitor synchronization primitive (generated by the C# compiler) that ensures thatonly one thread (at a time) is exclusively active in a critical section
The using statement in C# acquires one or more resources, executes a statement, andthen disposes of the resource(s)
EBNF
UsingStmt = "using" "(" ResourceAcquisition ")" EmbeddedStmt ResourceAcquisition = LocalVarDecl | Expr
Both the lock and using statements are covered in greater detail in Chapter 9 in the context
of threads and input/output, respectively
Trang 116.4 Exceptions and Exception Handling
Software developers have long realized that moving from a procedural to an oriented approach requires a completely different mindset Similarly, using exceptions,
object-as opposed to the traditional approach of returning flags, provides a completely ent and far more reliable method of tackling errors In this section, we present the C#exception-handling mechanism as a modern approach to robust error management Indoing so, we will show how exceptions:
differ-■ Separate error-handling code from normal code and
■ Make applications more readable and maintainable
6.4.1 What Is an Exception?
An exception is an unexpected error condition It is not a simple event, such as reaching
the end of an input stream For example, when the scanner of a compiler reads the nextcharacter from a file, you expect that it will eventually “reach the end.” This is expectedbehavior, as shown here:
while ( (c = inputStream.Read()) != EOF )
assembleToken(c);
inputStream.Close();
It is therefore important to note that an exceptionreally means an exceptional condition
that cannot be predicted The following are some examples:
■ Hardware failures,
■ Floppy disk ejected while reading,
■ Serial connection disconnected while transmitting, and
■ Resource exhaustion
Checking the above situations requires extensive manual polling and testing to ensurerobust behavior In fact, there are situations where testing and polling are simply inade-quate For example, reading a stream of binary values of different sizes, it is still possibleand unexpected to read past the end-of-file
while ( !inputStream.Eof() )
process( inputStream.GetStructure() );
inputStream.Close();
Trang 12Unexpected situations are not easy to determine However, it is important not to (ab)useexceptions as a way to report situations with simple and predictable behavior.
6.4.2 Raising and Handling Exceptions
Without exception handling, dealing with errors increases the length of the resultant code
often at the expense of its clarity Exception handling, on the other hand, is a mechanism
for dealing more systematically with exceptional error conditions It works by transferringexecution to a handler when an error occurs By separating the code that may generateerrors from the code that handles them, this mechanism allows the detection of errorswithout adding special conditions to test return values or flags
An exception is said to be raised (or thrown) when an unexpected error condition isencountered and it is said to be handled (or caught) at the point to which control is trans-ferred Appropriate action is then taken by the exception handler including rethrowing theexception to another handler, if necessary Because an exception unconditionally transferscontrol to an exception handler, the code within the block and beyond the point where theexception is raised is not reached
The System namespace contains the class Exception as the root of the handling hierarchy in the NET Framework The Exception class is composed of two imme-diate subclasses, SystemException and ApplicationException The SystemException class
exception-is defined as the base class for all predefined (.NET) system exceptions that are thrown bythe runtime system The ApplicationException class was originally intended to be used
as the base class for all application (user-defined) exceptions declared outside the NETFramework Because the code to handle user-defined exceptions is typically specific to
an application, it is very unlikely that an instance of ApplicationException will ever beneeded Although logically sound, in practice, ApplicationException adds an extraneousTip
layer to the exception hierarchy As a consequence, Microsoft strongly suggests that defined exceptions inherit directly from Exception rather than ApplicationException
user-A partial list of the most common system exceptions and where to define user-definedexceptions is given here:
Exception (root)
SystemExceptionArithmeticExceptionDivideByZeroExceptionOverflowExceptionFormatExceptionIndexOutOfRangeExceptionInvalidCastExceptionIOException
NullReferenceExceptionTypeLoadExceptionDllNotFoundExceptionEntryPointNotFoundException