Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 22 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
22
Dung lượng
363,6 KB
Nội dung
chapter 6 StatementsandExceptions I n 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 statementsand 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 opening and closing braces: EBNF 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 108 Chapter 6: StatementsandExceptions ■ then braces are required to enclose the two statements as a single block: if(x>0){ x = temp; Console.Write("Value of x is {0}.", x); } Finally, if code is not reachable within a block, then a compilation error is generated. 6.2 Declaration Statements 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 keyword const and the initialization must be explicit. Variables need not be explicitly initialized upon declaration since number, character, and reference-type variables are initialized by default to 0, ‘\0’, and null, respectively. The complete EBNF definition for declaration statements is given here: EBNF DeclStmt = ( LocalVariableDecl | LocalConstantDecl ) ";" . LocalVariableDecl = Type LocalVariableDecltorList . LocalVariableDecltor = Identifier ( "=" LocalVariableInitializer )? . LocalVariableInitializer = Expr | ArrayInitializer . LocalConstantDecl = "const" Type ConstantDecltorList . ConstantDecltor = Identifier "=" ConstantExpr . For example: float number; const String name = "Michel"; intn=2,m; int len[]={1,n,3}; 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 of the innermost block in which it is declared. For example: { . for(intn=0;n<8;n++) { // n is in the scope of the for . } . char c; // Declaration closer to its related code . } ■ 6.3 Embedded Statements 109 Finally, 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 statements, and iteration statements. A summary of these statementsand others in C# are listed in the following EBNF definition and described in the sections that follow. EBNF 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: EBNF ExprStmt = StmtExpr ";" . StmtExpr = InvocationExpr | ObjectCreationExpr | Assignment | PostIncExpr | PostDecExpr | PreIncExpr | PreDecExpr . An empty statement, on the other hand, is simply represented by a semicolon. EBNF EmptyStmt = ";" . 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. 110 Chapter 6: StatementsandExceptions ■ 6.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. if Statement The syntax of the if statement is:EBNF IfStmt = "if" "(" BooleanExpr ")" EmbeddedStmt1 ( "else" EmbeddedStmt2 )? . where the else part of the if statement is optional. If the condition specified by BooleanExpr evaluates to true then the action specified by EmbeddedStmt1 is performed. Otherwise, EmbeddedStmt2 is executed whenever present. Two variations of the if statement, with and without the else part, are illustrated in the following single example: bool directionUp; . if (directionUp) { // If statement with an else part if (++count > max) { // Nested if statement without an else part count = min; return true; } } else { if (--count < min) { // Nested if statement without an else part count = max; return true; } } return false; Another variation of the if statement uses the else if clause to select one of many alterna- tives. In the following example, this variation selects one of four alternatives based on the value of an operator character. A similar example using the switch statement is presented in the next subsection. char operator; . if (operator == ‘+’) { . } else if (operator == ‘-’) { . } else if (operator == ‘*’) { . ■ 6.3 Embedded Statements 111 } else { // Default case . } switch Statement The switch statement is used to choose one of many alternative actions based on the value of an expression Expr as shown here: EBNF 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 executed. Example: char op; long int1, int2; . switch (op) { 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); else Console.WriteLine("Divide by zero"); break; default: Console.WriteLine("Invalid operator: must be+-*x/"); break; } 112 Chapter 6: StatementsandExceptions ■ 6.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 the loop. 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: Console.Write("Countdown: "); int sec = 9; while ( sec >= 0 ) Console.Write("{0} ", sec--); Console.WriteLine(" . Go!"); Output: Countdown:9876543210 .Go! do-while Statement The syntax of the do-while loop is:EBNF DoStmt = "do" EmbeddedStmt "while" "(" BooleanExpr ")" ";" . EmbeddedStmt is executed one or more times until the BooleanExpr evaluates to false. Example (giving the same output): Console.Write("Countdown: "); int sec = 9; do Console.Write("{0} ", sec--); while ( sec >= 0 ); Console.WriteLine(" . Go!"); ■ 6.3 Embedded Statements 113 for Statement The syntax of the for loop is: EBNF ForStmt = "for" "(" ForInitializer? ";" ForCondition? ";" ForIterator? ")" EmbeddedStmt . and is equivalent to the following statements: ForInitializer "while" "(" ForCondition ")" "{" EmbeddedStmt ForIterator "}" where: EBNF ForInitializer = LocalVarDecl | StmtExprList . ForCondition = BooleanExpr . ForIterator = StmtExprList . Example (giving the same output): Console.Write("Countdown: "); for (int sec = 9; sec >= 0; --sec) Console.Write("{0} ", sec); Console.WriteLine(" . Go!"); An infinite for loop that prints dots: for (;;) Console.Write("."); is equivalent to the following while statement: while (true) Console.Write("."); foreach Statement The syntax of the foreach loop is: EBNF 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 114 Chapter 6: StatementsandExceptions ■ occurs if the variable is (1) modified via assignment or the ++ and -- operators or (2) passed as a ref or out parameter. Example: int[] evenNumbers={2,4,6,8}; foreach (int n in evenNumbers) Console.Write("{0} ", n); Console.WriteLine(); Output: 2468 6.3.4 Jump Statements C# offers five kinds of jump statements that unconditionally transfer control in an application: goto, continue, break, return, and exception handling (throw and try) state- ments. Because of its importance, exception handling is discussed separately in the next section. 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 local variables, 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 goto statement transfers control to any statement that is marked by a label including a case label as defined here: EBNF GotoStmt = "goto" ( Identifier | ("case" ConstantExpr) | "default" ) ";" . The goto statement must be within the scope of the label. Otherwise, a compilation error is generated. continue Statement The continue statement starts a new iteration of the innermost enclosing while, do-while, for,orforeach by prematurely ending the current iteration and proceeding with the next iteration, if possible. EBNF ContinueStmt = "continue" ";" . ■ 6.3 Embedded Statements 115 Example: for(inti=0;i<10;++i) { if(i%2==0)continue; Console.Write(i+""); } Console.WriteLine(); Output: 13579 The continue and goto statements are not recommended unless absolutely necessary Tip for improving the readability or optimizing the performance of a method. Justification, therefore, should be a well-thought-out compromise between clarity and efficiency. break Statement The break statement is used in labeled blocks, loops (while, do-while, for,orforeach), and switch statements in order to transfer control out of the current context, that is, the innermost enclosing block. EBNF BreakStmt = "break" ";" . Example (giving the same output as the for): Console.Write("Countdown: "); for (int sec = 9;;) { if (sec < 0) break; Console.Write("{0} ", sec--); } Console.WriteLine(" . Go!"); return Statement 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: EBNF ReturnStmt = "return" Expr? ";" . Example: using System; class Factorial { // non-void method must return a value static int Process(int i) { if (i > 0) // termination test return i * Process(i-1); // recursion invocation 116 Chapter 6: StatementsandExceptions ■ else return 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 with the return type specified by the method. For example, if 1.0 is returned instead of 1 in the previous example, then a compilation error is generated. Therefore, the static method Int32.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 for integral-type arithmetic operations and conversions. These statements were covered in Chapter 5. 6.3.6 lock and using Statements The lock statement delimits an embedded statement as a mutually exclusive, critical section 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 implemented with a monitor synchronization primitive (generated by the C# compiler) that ensures that only 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, and then 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. [...]... 6: Statements and Exceptions ■ Unexpected situations are not easy to determine However, it is important not to (ab)use exceptions as a way to report situations with simple and predictable behavior 6.4.2 Tip 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,... the exceptions are handled and a message is printed public class DeviceManager1 { public DeviceManager1() { dd = new LowLevelDeviceDriver(); } public void Init() { // When the exception is handled try { dd.Init(); } catch(DeviceException e) { Console.WriteLine("Handled in DeviceManager1 [{0}]", e); ■ 6.4 Exceptionsand Exception Handling 125 } catch(DivideByZeroException e) { Console.WriteLine("Handled... method Read is invoked and because status is initialized to false, the user-defined DeviceException is explicitly raised on line 15 and the following message is generated: Unhandled Exception: DeviceException: Cannot Read at Device.Read() at TestException1.Main() ■ 6.4.4 6.4 Exceptionsand Exception Handling 121 Using the try-catch Statement When the exceptions DivideByZeroException and DeviceException... LowLevelDeviceDriver given here invokes methods Read and Process Although dev can throw one of two exceptions, LowLevelDeviceDriver does not handle the exceptions and, instead, propagates these exceptions to DeviceManager1 public class LowLevelDeviceDriver { public LowLevelDeviceDriver() { dev = new Device(); } // When the exception is not handled and propagated up to the caller public void Init() {...■ 6.4 6.4 Exceptionsand Exception Handling 117 Exceptionsand Exception Handling Software developers have long realized that moving from a procedural to an objectoriented approach requires a completely different mindset Similarly, using exceptions, as opposed to the traditional approach of returning flags, provides a completely different and far more reliable method of tackling... raised try { } catch (SpecificException e) { // Handle a specific exception } catch (GenericException e) { // Handle a more generic exception } catch (Exception e ) { // Handles all exceptions } // // // // // From specific To more generic To most generic In general, it is a best practice for an application to only catch those exceptions that it can handle An OutOfMemoryException is a typical exception... here: try { } catch (ExceptionType2 e2) { throw new ExceptionType1 ( ); } Once an exception handler is found and the appropriate code is executed, execution resumes at the point following the try-catch statement that contains the final handler If no exception handler is found, a default exception handler is invoked and prints useful information about where the exception was thrown This information is also... third way preserves the original information of the Tip 124 EBNF Chapter 6: Statements and Exceptions ■ exception and, hence, the cause of the error by passing its reference to a new exception In this case, an instance of DeviceException is created using its third constructor (line 6 on page 119) by passing "Message" and e to msg and inner, respectively The complete EBNF definition of the try-catch statement... were raised in methods Process and Read, respectively, neither exception was caught and handled To associate a block of code within which an exception may occur with the appropriate exception handlers, a try-catch statement is used This statement clearly separates the code that raises an exception from the code that handles it try { // A block of code where one or more exceptions may be raised } catch... converted to, the class specified in the parameter of the exception handler Hence, to handle multiple exceptions as done above, the most specific catch block must precede a more generic one as shown next Otherwise, a compiler error is generated Clearly, the Exception root class catches all types of exceptionsand therefore must follow all other handlers Furthermore, only one catch block is executed for each . ments, selection statements, and iteration statements. A summary of these statements and others in C# are listed in the following EBNF definition and described. Read and Process. Although dev can throw one of two exceptions, LowLevelDeviceDriver does not handle the exceptions and, instead, propagates these exceptions