10.4 checked and unchecked operators and statements


10.4 checked and unchecked operators and statements

The checked keyword is useful for detecting overflows [5] in integral-type arithmetic operations and conversions.

[5] An overflow results when you are trying to store an integer value which is beyond the range of the integral variable. For example, the short type's range is only -32 768 to +32 767. An attempt to store a value greater than 32 767 into a short type causes an overflow. An attempt to store a value smaller than -32 768 into a short type causes an underflow. No distinction is made between underflow and overflow in this book. When I use the term 'overflow', I am referring to both overflow and underflow scenarios.

10.4.1 The checked keyword

The checked keyword can be used in one of two ways:

  • checked {<code block>}

  • checked (<expression>) ,

where <expression> or <code block> contains codes that perform any of the following operations (provided that operands are integral types only):

  • ++ and -- unary operators “ Example: int i = j++ ; where j is an int type;

  • “ unary operator “ Example: int i = -j ; where j is an int type;

  • +, - , * , / binary operators “ Example: int i = j + k ; where j and k are both int types;

  • explicit numeric conversions (or casting) from one integral type to another integral type “ Example: int i = (int)j ; where j is of a long type.

Statements within the () or {} brackets are considered to be within a checked context.

In cases where any of the above operations results in an overflow:

  • if the operation happens within a checked context, a System.OverflowException is thrown;

  • if the operation happens outside a checked context (i.e. within an unchecked context), no exception is thrown and the program continues. However, a 'wrong' value will be stored in the integral variable due to the overflow.

Study the program below.

 1: using System; 2: class MyClass{ 3:   public static void Main(){ 4:     int i = 32768; 5:     // a short can take up to +32767 only 6:  short s  =  (short)i;  7:     Console.WriteLine(s); 8:   } 9: } 

Output:

 c:\expt>test -32768 

When you try to explicitly cast 32768 into a short type, an overflow occurs. Nevertheless, because this is an explicit cast the compiler assumes that you know what you are doing, [6] and stores a 'wrong' value in s . In this case, it 'wraps around' by 1 and stores the smallest negative value a short can take.

[6] Which is obviously a bad assumption for some developers.

If you insert the checked operator, you can be notified via a System.OverflowException during runtime if an overflow does occur. Here is how the checked operator can be used:

 1: using System;  2: class MyClass{  3:   public static void Main(){  4:     int i = 32768;  5:  checked{  6:       short s = (short)i; // --> OverflowException  7:       Console.WriteLine(s);  8:  }  9:   } 10: } 

Output:

[View full width]
 
[View full width]
c:\expt>test Unhandled Exception: System.OverflowException: Arithmetic operation resulted in an graphics/ccc.gif overflow at MyClass.Main()

Within the checked context (lines 6 “ 7), an explicit cast from an int to a short happens (line 6). In this case, an overflow results and, since the cast occurs within a checked context, an OverflowException is thrown. If no overflow occurs during the cast, no exception is thrown. The exception error seen in the output is a runtime error, not a compilation error. If you change line 4 to:

 4:     int i = 100; 

the program executes properly and prints out 100 .

You should try to catch the exception and provide an appropriate exception handler. The program has been modified so that it exits gracefully. [7]

[7] This is a highly construed example structured in quite an unwieldy style. The purpose is simply to showcase how the checked operator works.

 1: using System;  2: class MyClass{  3:   public static void Main(){  4:     int i = 32768;  5:     try{  6:       checked{  7:         short s = (short)i; // --> OverflowException  8:         Console.WriteLine(s);  9:       } 10:     } 11:     catch (OverflowException e){ 12:         Console.WriteLine("exception caught:" + e); 13:     } 14:   } // end main 15: } // end class 

Output:

[View full width]
 
[View full width]
c:\expt>test exception caught: System.OverflowException: Arithmetic operation resulted in an overflow graphics/ccc.gif at MyClass.Main()

You can delimit the checked expression in round brackets too. The example below shows a method, DoThis() , which may throw the OverflowException if the expression x*y results in an overflow:

 40: public static int DoThis(int x, int y){ 41:  return  checked(x*y)  ; 42: } 

Line 41 may throw an OverflowException if the result of x*y is beyond the range of an int . The calling method may want to catch this exception and deal with it. Figure 10.1 summarizes what has been discussed so far.

Figure 10.1. Activity diagram showing flow of action when an integral overflow occurs.

graphics/10fig01.gif

If you are using csc.exe as your C# compiler, you can enforce overflow checking for all unmarked code using the /checked option:

 c:\expt>csc  /checked  test.cs 

Using the /checked option is equivalent to putting all your codes in a huge checked block. All your codes will be within a checked context except for those sections specifically marked as unchecked (see the next section).

10.4.2 The unchecked keyword

Study the following program.

 1: using System; 2: class MyClass{ 3:   public static void Main(){ 4:    // max positive value for int is 2147483647 5:  int i  =  2147483647;  6:  int j  =  i  +  1;  // overflow 7:    Console.WriteLine(j); 8:   } 9: } 

Output:

 -2147483648 

The class above compiles, performs an overflow operation (on line 6), and displays the results. Line 6 is outside a checked context and hence does not cause an OverflowException to be thrown. It is important to note that overflow checking (for the statement on line 6) is performed during runtime .

I have changed the program above slightly. Examine it carefully now:

 1: using System; 2: class MyClass{ 3:  public static void Main(){ 4:  int j  =  2147483647+1;  // overflow 5:    Console.WriteLine(j); 6:  } 7:} 

Compilation error:

 test.cs(4,13): error CS0220: The operation overflows at compile time in checked mode 

In this case, the class above fails to compile because of line 4. The only difference between the previous class and this one is that for this class, overflow checking on line 4 is performed during compile time . The reason for this is that the program is dealing with constant or fixed values, and it is possible to determine during compile time if an overflow occurs.

Let's alter the first class which successfully compiled by shifting the variable i outside the Main() method and declare it using the const modifier, so that it becomes a class constant:

 1: using System;  2: class MyClass{  3:   // i is a constant this time  4:  const int i  =  2147483647;  5:  6:   public static void Main(){  7:  int j  =  MyClass.i+1;  // overflow  8:    Console.WriteLine(j);  9:   } 10: } 

Compilation error:

 test.cs(7,12): error CS0220: The operation overflows at compile time in checked mode 

This time, compilation fails because the compiler is able to determine that an overflow is expected on line 7, since we are dealing with constants.

By default, if an overflow can be determined during compile time, you will not be able to compile it. In order to force your codes to compile, despite the fact that the compiler has determined an overflow even during compilation, you have to put the statements causing the predetermined overflow in an unchecked context.

This is accomplished by using the unchecked operator. Like the checked operator, you can use it in one of two ways:

  • unchecked {<code block>}

  • unchecked (<expression>) ,

where <expression> or <code block> contains codes that you want to place in an unchecked context.

Although seldom used, you suppress compile-time overflow checking with the unchecked keyword.

One last note before this section closes “ it is possible to nest checked sections within unchecked sections and vice versa. Whether overflow checking is performed on a statement or not depends on whether it is within a checked or unchecked context. The following program shows a checked block within an unchecked block.

 1: using System;  2: class MyClass{  3:   public static void Main(){  4:     int i = 32768;  5:  unchecked  {  6:       short s1 = (short)i; // no problem  7:       Console.WriteLine("checkpoint 1");  8:  9:  checked  { 10:         short s2 = (short)i; // --> OverflowException 11:         Console.WriteLine("checkpoint 2"); 12:       } 13:     } 14:   } // end Main 15: } 

Output:

[View full width]
 
[View full width]
c:\expt>test checkpoint 1 Unhandled Exception: System.OverflowException: Arithmetic operation resulted in an graphics/ccc.gif overflow at MyClass.Main()

In this example, lines 6 “ 7 are in an unchecked context, and lines 10 “ 11 are in a checked context.



From Java to C#. A Developers Guide
From Java to C#: A Developers Guide
ISBN: 0321136225
EAN: 2147483647
Year: 2003
Pages: 221
Authors: Heng Ngee Mok

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net