Chapter 7 Abnormal Processing
A great advantage of the General Language Runtime (CLR) is that exception handling is standardized in crosswords. An exception initiated in C # can be processed in Visual Basic customers. No longer HRESULTS or ISUPPORTERRORINFO interface.
Although the coverage of cross-language abnormal processing is very wide, this chapter completely discusses C # abnormal processing. You slightly change the overflow processing behavior of the compiler, and then interesting things start: you have handled this exception. To add more means, then trigger an exception you created.
7.1 Check and non-check statements (Checked and Unchecked Statements)
When you perform an operation, it is possible that the calculation result is exceeding the valid range of the result variable data type. This situation is called overflow, which you will be notified in some way in accordance with different programming languages - or not notified at all. (Is the C programmer sound familiar?)
So, how do C # handle overflow? To find out its default behavior, please see the example of the steps mentioned earlier in this book. (For convenience, the previous example is given in Listing 7.1)
Listing 7.1 Calculating a number of steps
1: USING System;
2:
3: Class Factorial
4: {
5: public static void main (string [] args)
6: {
7: long nfactorial = 1;
8: long ncomputeto = int64.parse (args [0]);
9:
10: long ncurdig = 1;
11: for (ncurdig = 1; ncurdig <= ncomputeto; ncurdig )
12: nfactorial * = ncurdig;
13:
14: console.writeLine ("{0}! Is {1}", ncomputeto, nfactorial;
15:}
16:}
When you use the command line to execute the program like this
Factorial 2000
The result is 0, and there is no happening. Therefore, assume that C # silently handles overflows and not clearly warned that you are safe.
By checking the entire application (compiler switch) or in the statement level, you can change this behavior. The following two sections solve a solution.
7.1.1 Compiler settings for overflow check
If you want to control overflow checking throughout the app, the C # Compiler setting option is what you want to find. By default, overflow check is disabled. To clearly request it, run the following compiler command:
CSC Factorial.cs / Checked
Now when you perform an application with a 2000 parameter, the CLR informs you that overflow exceptions (see Figure 7.1).
Figure 7.1 allows an overflow to abnormally, and the classification code has an exception.
Press the OK button to leave the dialog box appear:
Exception Occurred: System.OverflowException
At factorial.main (system.string [])
Now you understand the overflow condition triggered a system.overflowexception exception. In the next section, how to capture and handle the exceptions that appear after we complete the syntax check?
7.1.2 Syntax Overflow Check
If you don't want to overflow the entire application, you can only check for some code segments, which may be smooth. In this case, you may use check statements as displayed in Listing 7.2.
Overflow check in list 7.2 classification calculation
1: using system; 2:
3: Class Factorial
4: {
5: public static void main (string [] args)
6: {
7: long nfactorial = 1;
8: long ncomputeto = int64.parse (args [0]);
9:
10: long ncurdig = 1;
11:
12: for (ncurdig = 1; ncurdig <= ncomputeto; ncurdig )
13: checked {nfactorial * = ncurdig;
14:
15: console.writeLine ("{0}! Is {1}", ncomputeto, nfactorial;
16:}
17:}
Even if you use the logo checked-compiled the code, in line 13, overflow checks will still check the multiplication, because the checkcript has hosted it. The same error message will appear.
The statement that shows the opposite behavior is unchecked. Even if the overflow check is allowed (add Checked flag to the compiler), the code enclosed by the unchecked statement will not cause overflow exceptions:
unchecked
{
Nfactorial * = ncurdig;
}
7.2 Exception Processing Statement
Since you know how to generate an exception (you will find more methods, believe me), there is still a problem with how it handles it. If you are a C Win32 programmer, it must be familiar with SEH (structural exception handling). It is gratifying that the command in the C # is almost the same, and they also operate in a similar manner.
The following three sections describe the exception handling statement of the C #:
. Capture exceptions with try-catch
. Use try-finally to clear exception
. Treat all exceptions with try-catch-finally
7.2.1 capture exceptions using TRY and CATCH
You will definitely be very interested - don't give users a hateful abnormal message so that your application will continue. In this way, you must capture (handle) the exception.
The statement to use is try and catch. The TRY contains statements that may generate an exception, and Catch processes an exception, if there is an abnormality exists. Listing 7.3 Using TRY and CATCH to achieve exception processing for OverflowException.
Listing 7.3 Capture OverflowException triggered by Factorial Calculation
1: USING System;
2:
3: Class Factorial
4: {
5: public static void main (string [] args)
6: {
7: long nfactorial = 1, ncurdig = 1;
8: long ncomputeto = int64.parse (args [0]);
9:
10: TRY
11: {
12: checked
13: {
14: for (; ncurdig <= ncomputeto; ncurdig )
15: nfactorial * = ncurdig;
16:}
17:}
18: Catch (Overflowexception OE)
19: {
20: console.writeline ("Computing {0} Caused An Overflow Exception", NComputeTo; 21: Return;
twenty two: }
twenty three:
24: Console.WriteLine ("{0}! Is {1}", ncomputeto, nfactorial;
25:}
26:}
To illustrate clear, I extend some code segments, and I also guarantee that the exception is generated by the Checked statement, even when you forget the compiler settings.
As you can see, abnormal treatment is not bother. All you want to do is: In the TRY statement, it contains easy generation code, then captures exception, which is in this example. The OverflowException type. No matter when an exception is triggered, the code in the catch segment will pay attention to appropriate processing.
If you don't know which exception will be expected, but still want to be safe, simply ignore the type of exception.
Try
{
...
}
Catch
{
...
}
However, through this way, you cannot get access to an exception object, and the object contains important error information. Generalization exception handling code like this:
Try
{
...
}
Catch (System.exception E)
{
...
}
Note that you cannot transfer E objects to a method with REF or OUT modifiers, nor can it be a different value.
7.2.2 Use try and finally to clear anomalies
If you care more about cleaning instead of error handling, try and finally will get your favorite. Although it does not suppress error information, the code contained in the Finally block is still executed after an exception being thus.
Although the program is not terminated normally, you can also provide a message to the user, as shown in Listing 7.4.
Listing 7.4 Processing exceptions in a finally statement
1: USING System;
2:
3: Class Factorial
4: {
5: public static void main (string [] args)
6: {
7: long nfactorial = 1, ncurdig = 1;
8: long ncomputeto = int64.parse (args [0]);
9: bool ballfine = false;
10:
11: TRY
12: {
13: checked
14: {
15: for (; ncurdig <= ncomputeto; ncurdig )
16: nfactorial * = ncurdig;
17:}
18: Ballfine = true;
19:}
20: Finally
twenty one: {
22: if (! Ballfine)
23: Console.WriteLine ("Computing {0} Caused An Overflow Exception", NComputeTo;
24: ELSE
25: Console.Writeline ("{0}! Is {1}", ncomputeto, nfactorial;
26:}
27:}
28:}
By testing the code, you may guess that finaLY will be executed even if it does not cause an exception. This is true - the code in Finally is always executed, regardless of whether there is an abnormal condition. To illustrate how to provide some meaningful information to the user in both cases, I introduced a new variable Ballfine. Ballfine tells the finally block, whether it is called because of an exception or because the calculation is successfully completed. As a habit of SEH programmers, you may want to have a __leave statement equivalent statement with C . If you still don't know, here you explain: __leave statements in C are used to terminate the execution code in the TRY segment, and jump immediately to the Finally segment.
bad news! There is no __leave statement in the C #. However, the code in Listing 7.5 demonstrates a solution you can implement.
Listing 7.5 Jump from the TRY sentence to Finally statement
1: USING System;
2:
3: Class JumpTest
4: {
5: public static void main ()
6: {
7: TRY
8: {
9: console.writeline ("try");
10: goto __leave;
11:}
12: Finally
13: {
14: console.writeline ("finally");
15:}
16:
17: __leave:
18: console.writeline ("__ leave");
19:}
20:}
When this application is running, the output result is
Try
Finally
__leave
A goto statement cannot withdraw from a Finally block. Even put the goto statement in the TRY sentence block, or immediately return to the Finally block. Therefore, GOTO just leaves the TRY block and jumps to the FinalLunction. The __leave label can be reached until the code in finally is running. In this way, you can imitate the __leave statement used in SEH.
Because, you may doubt the goto statement is ignored because it is the last statement in the TRY statement and the control is automatically transferred to Finally. To prove that it is not the case, try the GOTO statement before the console.writeLine method call. Although there is a non-reachable code, you get a warning of the compiler, but you will see the goto statement is actually executed without generating the output of the "Try" string.
7.2.3 Treat all exceptions using try-catch-finally
The most possible way to apply the two error handling technologies in front - capture errors, clear, and continue the application. All you have to do is using Try, Catch, and Finally statements in the error handling code. Listing 7.6 shows the way to process zero errors.
Listing 7.6 Implement multiple CATCH statements
1: USING System;
2:
3: Class catchit
4: {
5: public static void main ()
6: {
7: TRY
8: {
Int nthezero = 0;
10: int NRESULT = 10 / NThezero;
11:}
12: Catch (DivideByzeroException Divex)
13: {
14: console.writeline ("Divide By Zero Occurred!");
15:} 16: Catch (Exception EX)
17: {
18: Console.writeline ("Some Other Exception");
19:}
20: Finally
twenty one: {
twenty two: }
twenty three: }
twenty four: }
The skill of this example is, which contains multiple catch statements. The first captured DivideByzeroException exception, and the second Catch statement handles all the remaining exceptions by capturing normal abnormalities.
You must always capture a specific exception first, then it is an ordinary exception. What happens if you caught anomalies in this order? The code in Listing 7.7 is described.
Listing 7.7 Catch statement in order inappropriate
1: TRY
2: {
3: int NThezero = 0;
4: int NRESULT = 10 / NThezero;
5:}
6: Catch (Exception EX)
7: {
8: console.writeline ("exception" ex.toT7tring ());
9: }
10: Catch (DivideByzeroException Divex)
11: {
12: console.writeline ("Never Going to See That";
13:}
The compiler will capture a small error and report this error like this:
Wrongcatch.cs (10, 9): Error CS0160: a Previous catch CLAUSE ALREADY
Catches All Exceptions of this or a super type ('system.exception ")
Means:
WrongCatch.cs (10, 9): Error Code CS0160: The front Catch statement has previously captured all the exceptions of this or advanced type ('system.exception').
Finally, I must report a shortcoming (or different) of the CLR exception to SEH: There is no equivalent of the Exception_Continue_Execution Identifier in the SEH exception filter. Basically, Exception_Continue_execution allows you to re-execute code snippet responsible for abnormalities. You have the opportunity to change variables before re-executing. I personally like the technology as: implement memory allocation as needed by using access violations.
7.3 Exception
When you have to capture anomalies, others must first initiate an exception. Moreover, not only others can trigger, you can also be responsible. It is quite simple:
Throw New ArgumentException ("Argument CAN't BE 5");
What you need is the throw statement and a suitable exception class. I have elected an exception for this example from the list provided by Table 7.1.
Table 7.1 Runtime Provided by Runtime
Exception Type Description Exception All Evergreen Based Class SysteMexception Running All Errors INDEXOFRANGEEXCEPTION This is raised when the subscript is run at the time of running NullReferenceException When an empty object is referenced when a null object is running InvalidOperationException When the call to the method is invalid to the current state of the object, the base class argumentnullexception that causes all parameter exceptions in the object is caused by the method of empty (not allowed), and the method is triggered by the method. ArgumentOutofRangeException When the parameter is not within a given range, the interopexception target is incremented by the interopexception target or the abnormal base class of the CLR outside the CLR. Comexception contains the exception of the COM class HRESULT information. The exception of the Win32 structure is abnormally handled information. Once, in Catch Inside the statement, you already have an exception disposed of, you don't have to create a new exception. There may be no exception in Table 7.1 that meets your special requirements - Why don't you create a new exception? In the junction, it is involved in these two topics.
7.3.1 Reproduction exception
When in the inside of a CATCH statement, you may decide to trigger an exception that is currently re-processing, further processing is left to some external TRY-CATCH statements. Examples of the method are shown in Listing 7.8.
Listing 7.8 Recommoding an exception
1: TRY
2: {
3: checked
4: {
5: for (; ncurdig <= ncomputeto; ncurdig )
6: nfactorial * = ncurdig;
7:}
8: }
9: catch (overflowexception oe)
10: {
11: console.writeline ("Computing {0} Caused An Overflow Exception", NComputeTo);
12: throw;
13:}
Note that I don't have to specify the declared abnormal variables. Although it is optional, you can also write this:
Throw OE;
Now we must pay attention to this exception.
7.3.2 Creating your own anomaly class
Although predefined exception classes are recommended, it is very convenient to create your own anomaly class in actual occasions. Create your own anomaly class, allowing your exception class to take different means according to the exception class.
The anomaly class Myimportantexception appears in Listing 7.9 follows two rules: First, it ends the class name with Exception. Second, it implements all three recommended universal structures. You should also follow these rules.
Listing 7.9 Implementing your own anomaly class MyImportantexception
1: USING System;
2:
3: Public class myimportantexception: Exception
4: {
5: public myimportantexception ()
6:: Base () {}
7:
8: Public MyImportantexception (String Message)
9:: Base (message) {}
10:
11: Public MyImportantexception (String Message, Exception Inner)
12:: Base (Message, Inner) {}
13:}
14:
15: Public Class ExceptionTestApp
16: {17: public static void testthrow ()
18: {
19: throw new myimportantexception ("Something Bad Has Happened.");
20:}
twenty one:
22: Public static void main ()
twenty three: {
24: TRY
25: {
26: ExceptionTestapp.testthrow ();
27:}
28: Catch (Exception E)
29: {
30: console.writeLine (e);
31:}
32:}
33:}
As you can see, MyImportantexception is unable to implement any special features, but it is entirely based on the System.Exception class. Test the new anomaly class with a Catch statement at the System.Exception class, the remainder of the program.
If there is no special implementation but only gives MyImportantexception defines three constructor, create it what does it make? It is an important type - you can use it in a catch statement, replacing more common anomaly. Customer code that may trigger your new exception can work according to the specified Catch code.
When writing a class library using your own namespace, you should also put an exception in the name space. Although it does not appear in this example, you should also use the appropriate properties to expand your exception class for extended error messages.
7.4 Ovenrepusted "To" and "Don't"
As the last advice, here is the list of abnormally caused and deal with what to do and do not do:
. To provide meaningful text, when an exception is thrown.
. To trigger an exception, only when the condition is really abnormal, that is, when a normal return value is not satisfied.
. To trigger an argumentException exception, if your method or attribute is passed a bad parameter.
. To trigger an InvalidOperationException exception, when the call operation is not suitable for the current state of the object.
. To trigger the most suitable exception.
. To use the linked list, they allow you to track an exception tree.
. Do not use an exception for normal or expected errors.
. Do not use an exception for the normal control of the process.
. Do not trigger NullReferenceException or IndexOutofRangeException in the method.
7.5 small knot
This chapter begins with an introduction overflow check. You can use the compiler switch (default is off) so that the entire application allows or disables overflow checks. If you need to fine-tuning, you can use check and non-check statements, which allow you to perform a code or without overflow check, although no application switch is set.
When an overflow occurs, an exception is triggered. How to deal with exception depends on you. I mentioned a variety of ways, including you most likely to use: Try, Catch, and Finally statements throughout the app. In a plurality of examples, you learned that it is different from Win32 structural exception (SEH).
User use for exception processing; however, if you are responsible for creating a new class, you can trigger an exception. There are several options: causing an early captured exception, causing the existing framework exception, or creates a new anomaly class in accordance with the specified actual goals.
Finally, you need to read each "want" and "don't" that triggered and handled an exception.