Exception Handling in ASP. NET (details)
Common Language Runtime (CLR) has a major advantage in that exception handling is cross-language standardized. An exception thrown in C # can be handled in Visual Basic. There are no HRESULTs or ISupportErrorInfo interfaces.
Despite the wide coverage of cross-language Exception Handling, this chapter focuses entirely on C # exception handling. To change the overflow processing behavior of the compiler, the interesting thing begins: You have handled the exception. You need to add more methods and then cause the exceptions you have created.
7.1 checked and unchecked statements
When you perform an operation, the calculation result may be out of the valid range of the Data Type of the result variable. This situation is called overflow. Depending on different programming languages, you will be notified in some way-or you will not be notified at all. (Do C ++ programmers sound familiar ?)
So, how does C # handle overflow? To find out its default behavior, see the factorial example I mentioned earlier in this book. (For convenience, the previous example is given again in listing 7.1)
Listing 7.1 calculates the factorial of a number
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 execute a program using a command line like this
Factorial 2000
The result is 0 and nothing happens. Therefore, imagine C # To handle overflow silently without explicitly warning you that it is safe.
You can change this behavior by allowing overflow verification at the statement level for the entire application (switched by the compiler. The following two sections provide a solution.
7.1.1 configure the compiler for overflow Verification
If you want to control overflow verification for the entire application, the C # compiler setting is exactly what you are looking. By default, overflow verification is disabled. To explicitly require it, run the following compiler command:
CSC factorial. CS/checked +
Now when you use the 2000 parameter to execute an application, CLR notifies you of an overflow exception (see Figure 7.1 ).
Figure 7.1 overflow exception is allowed, and the factorial code generates an exception.
Press OK to exit the dialog box to reveal the exception information:
Exception occurred: system. overflowexception
At factorial. Main (system. String &#;)
Now you know that the overflow condition causes a system. overflowexception exception. In the next section, how do we capture and handle exceptions after we complete the syntax validation?
7.1.2 syntax overflow check
If you do not want to allow overflow verification for the entire application and only allow verification for some code segments, you may be comfortable. In this case, you may use the verification statement as shown in listing 7.2.
Listing 7.2 overflow verification in factorial 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 flag checked-to compile the code, overflow verification will still check the multiplication implementation in row 13th. The error message is consistent.
The statement that shows the opposite behavior is not verified (unchecked ). Even if overflow verification is allowed (the checked + flag is added to the compiler), the code enclosed by the unchecked statement will not cause an overflow exception:
Unchecked
{
NFactorial * = nCurDig;
}
7.2 Exception Handling statement
Since you know how to generate an exception (you will find more methods, believe me), there are still problems with how to handle it. If you are a C ++ WIN32 programmer, you must be familiar with SEH (Structure Exception Handling ). You will find comfort in that the commands in C # are almost the same and they operate in a similar way.
The following three sections introduce C #'s exception-handling statements:
The following three sections describe C # Exception Handling statements:
. Catch exceptions with try-catch
. Use try-finally to clear exceptions
. Use try-catch-finally to handle all exceptions
7.2.1 catch exceptions using try and catch
You will certainly be very interested in one thing-do not prompt the user for the annoying exception message, so that your application can continue to execute. To do this, you must capture (handle) the exception.
In this way, try and catch are used. Try contains statements that may generate exceptions, while catch processes an exception. If an exception exists. Listing 7.3 uses try and catch as OverflowException to handle exceptions.
Listing 7.3 capturing OverflowException exceptions caused 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;
22 :}
23:
24: Console. WriteLine ("{0 }! Is {1} ", nComputeTo, nFactorial );
25 :}
26 :}
For clarity, I have extended some code segments, and I also ensure that exceptions are generated by the checked statement, even when you forget the compiler settings.
As you can see, exception handling is not troublesome. All you have to do is: Include the code that is prone to exceptions in the try statement, and capture the exception. This exception is of the OverflowException type in this example. No matter when an exception is thrown, the code in the catch segment must be properly processed.
If you do not know which exception will be expected, but want to be in a safe state, simply ignore the exception type.
Try
{
...
}
Catch
{
...
}
However, in this way, you cannot obtain access to the exception object, which contains important error information. The General Exception Handling Code is as follows:
Try
{
...
}
Catch (system. Exception E)
{
...
}
Note that you cannot use the ref or out modifier to pass the E object to a method or assign a different value to it.
7.2.2 use try and finally to clear exceptions
If you are more concerned with clearing, rather than error handling, try and finally will enjoy it. It not only suppresses error messages, but also executes all code contained in the Finally block after an exception is thrown.
Although the program is not terminated normally, you can obtain a message for the user, as shown in listing 7.4.
Listing 7.4 Exception Handling in finally statements
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
21 :{
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 detecting this code, you may guess that finally will be executed even if no exception is thrown. This is true-the code in finally will always be executed, regardless of whether the exception conditions exist. To illustrate how to provide meaningful information to users in two cases, I introduced the new variable bAllFine. BAllFine tells finally whether it is called because of an exception or just because of the successful completion of computing.
As a programmer accustomed to SEH, you may wonder if there is a statement equivalent to the _ leave Statement, which is useful in C ++. If you do not know, the _ leave statement in C ++ is used to terminate the Execution Code in the try Language Segment in advance and immediately jump to the finally Language Segment.
Bad message. C # does not have the _ leave statement. However, the Code in listing 7.5 demonstrates a solution that you can implement.
Listing 7.5 redirects from a try statement to a 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 the application is running, the output result is
Try
Finally
_ Leave
A goto statement cannot exit a finally segment. Even if the GOTO statement is placed in the try statement segment, the return will be immediately controlled to the finally segment. Therefore, Goto only leaves the try CIDR Block and jumps to the finally CIDR block. The _ leave tag can be reached only after the finally code is completed. In this way, you can imitate the _ leave statement used in Seh.
By the way, you may suspect that 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 this is not the case, put the GOTO statement before calling the console. writeline method. Although you get a warning from the compiler because the Code cannot be reached, you will see that the GOTO statement is actually executed and there is no output for the try string.
7.2.3 use try-catch-finally to handle all exceptions
The most likely way for an application is to merge the first two error handling techniques-capture, clear, and continue executing the application. All you need to do is use try, catch, and finally statements in the error handling code. Listing 7.6 shows how to handle the zero division error.
Listing 7.6 implements multiple catch statements
1: using system;
2:
3: Class catchit
4 :{
5: public static void main ()
6 :{
7: Try
8 :{
9: 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
21 :{
22 :}
23 :}
24 :}
The trick in this example is that it contains multiple catch statements. The first captures dividebyzeroexception exceptions that are more likely to occur, and the second catch statement processes all the remaining exceptions by capturing Common exceptions.
You must always capture specific exceptions first, followed by common exceptions. If you do not capture exceptions in this order, what will happen? The code in listing 7.7 is described.
Listing 7.7 catch statements with inappropriate order
1: Try
2 :{
3: int nthezero = 0;
4: int nresult = 10/nthezero;
5 :}
6: catch (Exception Ex)
7 :{
8: Console. WriteLine ("exception" + Ex. ToString ());
9 :}
10: catch (DivideByZeroException divEx)
11 :{
12: Console. WriteLine ("never going to see that ");
13 :}
The compiler will capture a small error and report it like this:
Wrongcatch. cs (10, 9): error CS0160: A previous catch clause already
Catches all exceptions of this or a super type ('System. exception ')
Finally, I have to report a disadvantage (or difference) of the CLR exception compared to SEH: There is no equivalence of the prediction_continue_execution identifier, which is useful in the SEH exception filter. Basically, prediction_continue_execution allows you to re-execute the code snippets responsible for exceptions. Before re-execution, you have the opportunity to change the variables. My personal favorite technology is to allocate memory as needed due to access violation exceptions.
7.3 exception
When you must capture exceptions, others must first be able to cause exceptions. In addition, not only can it be triggered by others, but you can also be responsible for triggering. It is quite simple:
Throw new ArgumentException ("Argument can't be 5 ");
What you need is a throw statement and an appropriate exception class. I have selected an exception from the list provided in table 7.1 for this example.
Table 7.1 standard exceptions provided by Runtime
Exception type description
Exception: base class of all Exception objects
Base classes of all errors generated during systemexception running
Indexoutofrangeexception is triggered when the subscript of an array exceeds the range
Nullreferenceexception when an empty object is referenced
Invalidoperationexception when a call to a method is invalid for the current state of the object, it is triggered by some methods.
Argumentexception base class with all parameter exceptions
Argumentnullexception is triggered by a method when the parameter is null (not allowed ).
Argumentoutofrangeexception is triggered by a method when the parameter is not within the specified range.
Base class of the interopexception target that is located in or occurs outside the CLR Environment
An exception occurs when comexception contains the hresult information of the COM class.
Sehexception encapsulates Win32 structure Exception Handling Information exceptions
However, you do not have to create a new exception if you have any exceptions in the catch statement. None of the exceptions in Table 7.1 meet your special requirements-why not create a new exception? These two topics are involved in the upcoming section.
7.3.1 re-exception
When it is inside a catch statement, you may decide to trigger an exception that is currently being processed again, leaving further processing for some external try-catch statements. An example of the method is shown in listing 7.8.
Listing 7.8 re-raises 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: I do not need to specify the declared exception variable. Although it is optional, you can write as follows:
Throw oe;
Now you must pay attention to this exception.
7.3.2 create your own exception class
Although we recommend that you use a predefined exception class, it may be convenient to create your own exception class. Create your own exception class, allowing users of your exception class to take different measures based on the exception class.
The Exception class MyImportantException in listing 7.9 follows two rules: first, it ends the class name with Exception. Second, it implements all three recommended general structures. You should also follow these rules.
Listing 7.9 implements its own exception 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 :}
21:
22: public static void Main ()
23 :{
24: try
25 :{
26: ExceptionTestApp. TestThrow ();
27 :}
28: catch (Exception e)
29 :{
30: Console. WriteLine (e );
31 :}
32 :}
33 :}
As you can see, the MyImportantException Exception class cannot implement any special functions, but it is completely based on the System. Exception class. Test the new Exception class for the remaining part of the program, and use a catch statement for the System. Exception class.
If there are no special implementations but three constructors are defined for MyImportantException, what is the significance of creating them? It is an important type-you can use it in catch statements to replace more common exception classes. The customer code that may cause your new exception can be used according to the prescribed catch code.
When you use your own namespace to compile a class library, you also need to place exceptions in this namespace. Although it does not appear in this example, you should still use the appropriate attributes to expand your exception classes for extended error messages.
7.4 "yes" and "no" for Exception Handling"
As the last piece of advice, here is a list of what to do and not to handle exceptions:
. When an exception is thrown, you must provide meaningful text.
. An exception is thrown only when the condition is true, that is, when a normal return value is not met.
. If your method or attribute is passed with a bad parameter, an ArgumentException exception is thrown.
. When the call operation is not suitable for the current state of the object, an InvalidOperationException must be thrown.
. The most suitable exception to be thrown.
. To use link exceptions, they allow you to track the exception tree.
. Do not use exceptions for normal or expected errors.
. Do not use exceptions for normal flow control.
. Do not cause NullReferenceException or IndexOutOfRangeException in the method.
Conclusion 7.5
This chapter begins with the introduction of overflow verification. You can use the compiler switch (off by default) to enable or disable overflow verification for the entire application. If you need to fine-tune the control, you can use the checksum and non-checksum statement, which allows you to use or do not use overflow verification to execute a piece of code, although no switch is set for the application.
When overflow occurs, an exception is thrown. How to handle exceptions depends on you. I have proposed various approaches, including try, catch, and finally statements that are most likely to run through the entire application. In the accompanying examples, you learned the difference between it and the WIN32 structure Exception Handling (SEH.
Exception Handling is for class users. However, if you create a new class, an exception can be thrown. There are multiple options: to cause exceptions that have already been captured, to cause framework exceptions, or to create new exception classes based on the specified actual target.
Finally, you need to read the various "yes" and "no" that cause and handle exceptions ".