Thoughts on. NET Exception Handling and. net Exception Handling
The next year is approaching. For most programmers, they can be idle for a while now. However, in this free time, they can only argue Which language is better at killing time, it is estimated that there will be a lot of questions about java and. net blog post appears, I said to serve as a fool, quietly watching the big guys to express their feelings.
The above nonsense is enough, so I will not talk nonsense here. Let's start with the question.
In project development, system and code stability and fault tolerance are both required. The difference between the code in the actual development project and the sample code is more about the stability, fault tolerance, and scalability of code running. Because for implementing a function, the core code for implementing the function is the same and may only be optimized in writing. However, for classes used to implement a certain operation, this is the same in most cases. In this case, there are many issues that need to be taken into account in the actual development process, not only the implementation of a specific function, but also the stability and scalability of the Code.
The above are issues that need to be faced during actual development. In my recent blog post, I am also considering how to write this exception and how to understand it, I hope to help you and welcome your ideas and comments to share your knowledge and opinions.
I. Overview of DotNET exceptions:
When talking about exceptions, we need to know what exceptions are. If we want to learn everything, we should know what things we want to learn, so that we can have a general understanding in our hearts. An exception is an action that a member can complete without its name claim. In. in. NET, no error code can be returned for constructors, obtaining and setting properties, adding and deleting events, Calling operator overloading, or calling conversion operators, however, if you need to report errors in these structures, you must provide an exception handling mechanism.
In exception handling, we often use the following three blocks: try block, catch Block, and finally block. These three blocks can be used together, or they can be used without writing catch blocks. Exception Handling blocks can be nested for use. The specific methods are described below.
In the exception handling mechanism, there are generally three options: Re-throwing the same exception, notifying the code at the higher layer of the call stack of the exception; throwing a different exception, you want to call the code at the higher layer of the stack to provide richer exception information, and let the thread exit from the bottom of the catch Block.
There are some guiding suggestions on how to handle exceptions.
1. Use finally blocks properly:
The finally block can be executed no matter what type of exception thrown by the thread. The finall block is generally used to clean up the operations that have been successfully started, and then return the code after the caller or finally block.
2. Exception capture should be appropriate:
Why should exceptions be captured properly? The following code cannot capture all exceptions. After capturing exceptions, we need to handle these exceptions. If we capture all exceptions, however, we have no way to handle exceptions that are not foreseen.
If the application code throws an exception, the other end of the application may be expected to catch the exception. Therefore, it cannot be written as an exception block of "size-fits-all, this exception should be allowed to move up in the call stack so that the application code can handle the exception accordingly.
You can use System. Exception to catch exceptions in a catch block, but it is best to throw an Exception again at the end of the catch Block. The reason will be explained later.
Try {var hkml = GetRegistryKey (rootKey); var subkey = hkml. CreateSubKey (subKey); if (subkey! = Null & keyName! = String. empty) subkey. setValue (keyName, keyValue, RegistryValueKind. string);} catch (Exception ex) {Log4Helper. error ("create registry Error" + ex); throw new Exception (ex. message, ex );}
3. recover from an exception:
After capturing exceptions, we can write specific exception recovery code so that the program can continue to run. When capturing exceptions, You need to capture specific exceptions, fully grasp the situations in which exceptions will be thrown, and know the types derived from the caught exception types. Do not handle or catch the System. Exception unless an Exception is thrown again at the end of the catch Block.
4. maintenance status:
Generally, when we complete an operation or method, we need to call several methods in combination to complete the operation. During the execution process, the previous methods are completed, and the subsequent methods are abnormal. Partial rollback is completed when an unrecoverable exception occurs. Because we need to recover information, all exception information needs to be captured when an exception is captured.
5. Hide implementation details to maintain the contract:
Sometimes you may need to capture an exception and throw a different exception. This can maintain the contract of the method, and the heart exception type thrown should be a specific exception. See the following code:
FileStream fs = null; try {fs = FileStream ();} catch (FileNotFoundException e) {// throw a different exception that contains the exception information, set the original exception to an internal exception throw new NameNotFoundException ();} catch (IOException e) {// throws a different exception and contains the exception information, set the original exception to an internal exception throw new NameNotFoundException ();} finally {if (fs! = Null) {fs. close ();}}
The above Code only describes a processing method. All thrown exceptions should be passed up along the call stack of the method instead of "swallowed up" and a new exception should be thrown. If a Type constructor throws an exception that is not captured in the Type constructor method, CLR will capture the exception internally and throw a new typeinitialtionexception instead.
Ii. Common DotNET exception handling mechanisms:
After an exception occurs in the code, we need to handle this exception. if an exception is not handled in a timely manner, the CLR will terminate the process. In exception handling, we can capture exceptions in one thread and throw exceptions again in another thread. When an exception is thrown, the CLR will look up the catch Block that matches the thrown exception type in the call stack. If no catch Block matches the exception type thrown, an unhandled exception occurs. The CLR will terminate the process if it detects that any thread in the process has a single processing exception.
1. Exception Handling block:
(1). try block: Usually some general resource cleanup operations are required to contain code, or to recover from exceptions, or both. The try block can also contain code that may throw exceptions. A try block must have at least one associated catch block or finall block.
(2). catch Block: contains the code to be executed in response to an exception. The expressions in parentheses after the catch keyword are of the capture type. The capture type is specified from System. Exception or its derived class. CLR searches for a matched catch Block from top to bottom. Therefore, the exception of the instruction body should be placed on the top. Once the CLR finds a catch Block that matches the capture type, it will execute the code in all the finally blocks in the inner layer. "inner finally" refers to the start of the tey block that throws an exception, all finally blocks between catch blocks matching exceptions.
Use System. after an Exception is caught, you can throw an Exception at the end of the catch Block. If the Exception is caught, the program is not processed or terminated in time, this Exception may pose a major security risk to the program. The Exception class is the base class of all exceptions and can capture all exceptions in the program. If a large Exception occurs, we did not deal with the problem in a timely manner, resulting in a huge number of problems.
(3). finally block: The included code is the code that will be executed. After all code of the finally block is executed, the thread exits from the finally block and runs the statement following the finally block. If no finally block exists, the thread starts executing the statement after the last catch block.
Note: exception blocks can be combined and nested. Examples of the three exception blocks are not described here, exception nesting can prevent unhandled exceptions from being processed again.
2. Exception Handling instance:
(1). Exception Handling extension method:
/// <Summary> /// format the exception message /// </summary> /// <param name = "e"> exception object </param> /// <param name = "isHideStackTrace"> whether to hide the abnormal scale Information </param> // <returns> formatted Exception string </returns> public static string FormatMessage (this Exception e, bool isHideStackTrace = false) {var sb = new StringBuilder (); var count = 0; var appString = string. empty; while (e! = Null) {if (count> 0) {appString + = "";} sb. appendLine (string. format ("{0} exception message: {1}", appString, e. message); sb. appendLine (string. format ("{0} exception type: {1}", appString, e. getType (). fullName); sb. appendLine (string. format ("{0} exception method: {1}", appString, (e. targetSite = null? Null: e. targetSite. name); sb. appendLine (string. format ("{0} abnormal source: {1}", appString, e. source); if (! IsHideStackTrace & e. StackTrace! = Null) {sb. AppendLine (string. Format ("{0} exception Stack: {1}", appString, e. StackTrace);} if (e. InnerException! = Null) {sb. appendLine (string. format ("{0} internal exception:", appString); count ++;} e = e. innerException;} return sb. toString ();}
(2). Verification exception:
/// <Summary> /// check whether the string is empty or empty, and throw an exception /// </summary> /// <param name = "val"> value test </param> /// <param name = "paramName"> Parameter check the name </param> public static void CheckNullOrEmpty (string val, string paramName) {if (string. isNullOrEmpty (val) throw new ArgumentNullException (paramName, "Value can't be null or empty ");} /// <summary> /// check whether the parameter is null or not, and throw an exception /// </summary> /// <param name = "param"> check value </param> /// <param name = "paramName"> parameter name </param> public static void CheckNullParam (string param, string paramName) {if (string. isNullOrEmpty (param) throw new ArgumentNullException (paramName, paramName + "can't be neither null nor empty"); }/// <summary >/// check whether the parameter is invalid, and throw an exception /// </summary> /// <param name = "param"> check value </param> /// <param name = "paramName"> Parameter name </param> public static void CheckNullParam (object param, string paramName) {if (param = null) throw new ArgumentNullException (paramName, paramName + "can't be null ");} /// <summary> /// check that parameter 1 is different from parameter 2 /// </summary> /// <param name = "param1"> value 1 test </ param> /// <param name = "param1Name"> name of value 1 </param> // <param name = "param2"> value 2 to test </param> /// <param name = "param2Name"> name of vlaue 2 </param> public static void CheckDifferentsParams (object param1, string param1Name, object param2, string param2Name) {if (param1 = param2) {throw new ArgumentException (param1Name + "can't be the same as" + param2Name, param1Name + "and" + param2Name) ;}/// <summary> // check whether an integer is positive (0 or greater) /// </summary> /// <param name = "val"> integer test </param> public static void PositiveValue (int val) {if (val <0) throw new ArgumentException ("The value must be greater than or equal to 0. ");}
(3). Try-Catch extension operation:
/// <Summary> /// perform the specified and subsequent functions on an object, handle exceptions // </summary> // <typeparam name = "T"> Object Type </typeparam> // <param name = "source"> Value </param> /// <param name = "action"> main function code to be executed on the value </param> /// functions in <param name = "failureAction"> catch code </param> /// <param name = "successAction"> Function Code executed after successful master Function Code </param> /// <returns> whether the master function code is successfully executed </returns> public static bool TryCatch <T> (this T source, action <T> action, Action <Exception> failureAction, Action <T> successAction) where T: class {bool result; try {action (source); successAction (source ); result = true;} catch (Exception obj) {failureAction (obj); result = false;} return result ;} /// <summary> /// execute the specified function on an object, handle exceptions // </summary> // <typeparam name = "T"> Object Type </typeparam> // <param name = "source"> Value </param> /// <param name = "action"> main function code to be executed on the value </param> /// functions in <param name = "failureAction"> catch code </param> // <returns> whether the main function code is successfully executed </returns> public static bool TryCatch <T> (this T source, action <T> action, Action <Exception> failureAction) where T: class {return source. tryCatch (action, failureAction, obj =>{}) ;}/// <summary> // you can specify a function for an object, handle exceptions and return values // </summary> /// <typeparam name = "T"> Object Type </typeparam> /// <typeparam name = "TResult"> return value type </typeparam> /// <param name = "source"> value </param> /// <param name = "func"> main function to perform on values code </param> /// <param name = "failureAction"> function code in catch </param> /// <param name = "successAction"> run the main function code after successful execution </param> /// <returns> return value of the function code, if an exception occurs, return the default value of the object type </returns> public static TResult TryCatch <T, TResult> (this T source, Func <T, TResult> func, action <Exception> failureAction, Action <T> successAction) where T: class {TResult result; try {var u = func (source); successAction (source); result = u ;} catch (Exception obj) {failureAction (obj); result = default (TResult);} return result ;}/// <summary> // you can specify a function for an object, handle exceptions and return values // </summary> /// <typeparam name = "T"> Object Type </typeparam> /// <typeparam name = "TResult"> return value type </typeparam> /// <param name = "source"> value </param> /// <param name = "func"> main function to perform on values code </param> /// <param name = "failureAction"> function code in catch </param> /// the return value of the <returns> function code, if an exception occurs, return the default value of the object type </returns> public static TResult TryCatch <T, TResult> (this T source, Func <T, TResult> func, action <Exception> failureAction) where T: class {return source. tryCatch (func, failureAction, obj => {});}
This article does not specifically introduce the use of try, catch, and finally, but provides some common methods. Generally, developers have an understanding of the use of the three blocks, we will not repeat the introduction.
Iii. DotNET Exception analysis:
CLR allows exceptions to throw any type of instances. Here we introduce a System. Exception class:
1. Message attribute: indicates the cause of the exception.
[__DynamicallyInvokable]public virtual string Message{ [__DynamicallyInvokable] get { if (this._message != null) { return this._message; } if (this._className == null) { this._className = this.GetClassName(); } return Environment.GetRuntimeResourceString("Exception_WasThrown", new object[] { this._className }); }}
The code above shows that Message only has the get attribute, so message is read-only. GetClassName () gets the exception class. GetRuntimeResourceString () gets the resource string at runtime.
2. StackTrace attribute: contains the names and signatures of all methods called before an exception is thrown.
public static string StackTrace{ [SecuritySafeCritical] get { new EnvironmentPermission(PermissionState.Unrestricted).Demand(); return GetStackTrace(null, true); }}
EnvironmentPermission () is used for Environment restrictions. PermissionState. Unrestricted sets the permission status and GetStackTrace () gets the stack trace. For details, refer to the code of GetStackTrace.
internal static string GetStackTrace(Exception e, bool needFileInfo){ StackTrace trace; if (e == null) { trace = new StackTrace(needFileInfo); } else { trace = new StackTrace(e, needFileInfo); } return trace.ToString(StackTrace.TraceFormat.Normal);}
public StackTrace(Exception e, bool fNeedFileInfo){ if (e == null) { throw new ArgumentNullException("e"); } this.m_iNumOfFrames = 0; this.m_iMethodsToSkip = 0; this.CaptureStackTrace(0, fNeedFileInfo, null, e);}
The above is the specific implementation of the stack tracing method, which is mainly used for debugging.
3. GetBaseException () method for obtaining basic exception information.
[__DynamicallyInvokable]public virtual Exception GetBaseException(){ Exception innerException = this.InnerException; Exception exception2 = this; while (innerException != null) { exception2 = innerException; innerException = innerException.InnerException; } return exception2;}
The InnerException attribute is an internal exception, which is a virtual method and is overwritten here. Take a look at the InnerException attribute.
[__DynamicallyInvokable]public Exception InnerException{ [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get { return this._innerException; }}
4. ToString () format the exception information.
private string ToString(bool needFileLineInfo, bool needMessage){ string className; string str = needMessage ? this.Message : null; if ((str == null) || (str.Length <= 0)) { className = this.GetClassName(); } else { className = this.GetClassName() + ": " + str; } if (this._innerException != null) { className = className + " ---> " + this._innerException.ToString(needFileLineInfo, needMessage) + Environment.NewLine + " " + Environment.GetRuntimeResourceString("Exception_EndOfInnerExceptionStack"); } string stackTrace = this.GetStackTrace(needFileLineInfo); if (stackTrace != null) { className = className + Environment.NewLine + stackTrace; } return className;}
In this method, the obtained exception information is formatted as a string. this. GetClassName () gets information about the exception class.
We have noticed [_ DynamicallyInvokable] Custom Attributes. Let's take a look at the specific implementation code:
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]public __DynamicallyInvokableAttribute(){}
For more information about the attribute "Image Boundary", see Via CLR c.
Iv. Summary:
In the above introduction to exceptions, we mainly introduce the CLR Exception Handling Mechanism, some common Exception Code, and the Exception class. In actual projects, we generally do not directly throw exceptions to the customer. When writing a program, we have considered program fault tolerance. After the program detects exceptions, we try to restore the program as much as possible, or write the exception information into the log to open the program to the error page. If a serious exception occurs, the exception is thrown and the program is terminated.
I hope it will be helpful for everyone's learning, and I hope you can support the house of helping customers more.