This article mainly introduces about. NET exception processing thinking summary, with a certain reference value, interested in small partners can refer to.
Approaching, for most programmers, can be idle for a while, but in this free time, only to argue which language is better to kill time, it is estimated that there will be a lot of Java and. NET blog recently appeared, I said to eat as a melon masses, quietly watching the big boys to express their feelings.
Above the nonsense said enough, here is no longer nonsense, or cut to the chase.
In the project development, there are corresponding requirements for the stability and fault tolerance of the system and code. The difference between the code in the actual development project and the sample code is the comparison of the stability, fault tolerance and extensibility of the code running. Because the core code for implementing functionality is the same for implementing a feature, it may just be optimized for writing, but it is most often the same for classes that implement an operation. In this way, in the actual development process, we need to consider more issues, has not only been limited to a specific function implementation, more is the code stability and extensibility considerations.
The above is in the actual development needs to face the problem, the author in the recent blog post, also consider how this anomaly needs to write, and how the exception needs to understand, hope to have a help, but also welcome everyone to put forward their ideas and opinions, share their knowledge and insights.
I. Overview of dotnet anomalies:
When it comes to exceptions, we need to know what is called an anomaly, and if we want to learn, we should know what we are going to learn, so there is a general understanding in the mind. An exception is an action that a member does not complete by declaring its name to be complete. In. NET, constructors, get and set properties, add and delete events, call operator overloads, call conversion operators, and so on, have no way to return an error code, but in these constructs you need to report an error, and you must provide an exception handling mechanism.
In the handling of exceptions, the three blocks we often use are: try block; catch block; finally block. These three blocks can be used together, or can not write catch block use, exception handling blocks can be nested use, the specific method is described below.
In the exception handling mechanism, there are generally three options: to re-throw the same exception, the call stack to the higher layer of code to notify the occurrence of the exception, throw a different exception, want to call the stack layer of code to provide richer exception information; Let the thread exit from the bottom of the catch block.
There are some guiding suggestions for how exceptions are handled.
1. Proper use of the finally block:
The finally block guarantees that any type of exception that is thrown by the pipeline can be executed, and the Finall block is generally used to clean up those operations that have been successfully started before returning the code after the caller or finally block.
2. The exception capture needs to be appropriate:
Why is it appropriate to catch an exception? The following code, because we can not catch anything exception, after catching the exception, we need to deal with these exceptions, if we catch all the exceptions, but there is no foreseeable anomalies, we have no way to deal with these exceptions.
If the application code throws an exception, the other end of the application may expect to catch the exception, so it cannot be written as a "size-all" exception block, which should allow the exception to move up in the call stack, allowing the application code to handle the exception in a targeted manner.
In a catch block, you can use System.Exception to catch an exception, but it is best to re-throw the exception at the end of the catch block. The reasons are 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 ("Creating Registry Error" + ex);
throw new Exception (ex.Message, ex);
}
3. Recover from the exception:
After catching the exception, we can write some exception recovery code to allow the program to continue to run. When catching exceptions, you need to catch specific exceptions, fully understand the circumstances under which an exception will be thrown, and know which types are derived from the caught exception type. Do not handle or catch System.Exception unless the exception is rethrown at the end of the catch block.
4. Maintain status:
In general, when we complete an operation or a method, we need to call several method combinations to complete it. During the execution process, the previous methods are completed, and the latter methods are abnormal. When an unrecoverable exception occurs, the partially completed operation is rolled back because we need to recover the information, so when we catch the exception, we need to capture all the exception information.
5. Hiding implementation details to maintain the contract:
Sometimes it may be necessary to catch an exception and re-throw a different one. In this way, the contract of the method can be maintained, and the type of heart exception thrown should be a specific exception. Look at the following code:
FileStream fs = null;
try
{
fs = FileStream ();
}
catch (FileNotFoundException e)
{
// Throw a different exception, include the exception information in it, and set the original exception as an internal exception
throw new NameNotFoundException ();
}
catch (IOException e)
{
// Throw a different exception, include the exception information in it, and set the original exception as an internal exception
throw new NameNotFoundException ();
}
finally
{
if (fs! = null)
{
fs.close ();
}
}
The above code is just one way to deal with it. All exceptions thrown should be passed up the method's call stack, rather than throwing a new exception after they have been "swallowed". If a type constructor throws an exception and the exception is not caught in the type constructor method, the CLR catches the exception internally and throws a new TypeInitialztionException instead.
Two. DotNET exception common processing mechanism:
After an exception occurs in the code, we need to handle the exception. If an exception is not handled in a timely manner, the CLR will terminate the process. In exception handling, we can catch exceptions in one thread and rethrow them in another thread. When an exception is thrown, the CLR looks up in the call stack for a catch block that matches the type of exception thrown. If no catch block matches the type of exception thrown, an unhandled exception occurs. The CLR detects that any thread in the process has a bit handling exception and will terminate the process.
1. Exception handling block:
(1) .try block: Contained code usually needs to perform some general resource cleanup operations, or needs to recover from exceptions, or both. The try block can also contain code that might throw an exception. A try block has at least one associated catch or final block.
(2) .catch block: Contains the code that needs to be executed in response to an exception. The expression in parentheses after the catch keyword is a catch type. The catch type is specified from System.Exception or its derived classes. The CLR searches for a matching catch block from top to bottom, so specific exceptions should be taught at the top. Once the CLR finds a catch block with a matching capture type, it executes the code in all finally blocks in the inner layer. "Inner finally" refers to all the finally between the catch block that throws the exception and the catch block that matches the exception. Piece.
After using System.Exception to catch an exception, you can use the exception to be re-thrown at the end of the catch block, because if we do not process or terminate the program in time after catching the Exception, this exception may cause great security risks to the program. The Exception class is the base class for all exceptions. It can catch all exceptions in the program. If a large exception occurs, we do not deal with it in a timely manner, and the problem caused is huge.
(3) .finally block: the included code is guaranteed to be executed. After all the code in the finally block is executed, the thread exits the finally block and executes the statement immediately after the finally block. If there is no finally block, the thread will execute from the statement after the last catch block.
Remarks: The exception blocks can be combined and nested. For the example of three exception blocks, we will not introduce them here. The nesting of exceptions can prevent unhandled exceptions from appearing again when handling exceptions, which will not be repeated here. .
2. Examples of exception handling:
(1). Exception processing 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> The formatted exception information 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} exception 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 if the string is empty or empty and throw an exception
/// </ summary>
/// <param name = "val"> value test </ param>
/// <param name = "paramName"> parameter check 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>
/// Please check that the parameter is not empty or empty 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 that the parameter is not 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>
/// Please 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 if an integer value 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>
/// Execute the specified function and subsequent functions on an object, and handle abnormal situations
/// </ summary>
/// <typeparam name = "T"> Object type </ typeparam>
/// <param name = "source"> value </ param>
/// <param name = "action"> The main function code to be executed on the value </ param>
/// <param name = "failureAction"> Function code in catch </ param>
/// <param name = "successAction"> Function code executed after the main function code succeeds </ param>
/// <returns> whether the main function code is executed smoothly </ 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 and handle the exception
/// </ summary>
/// <typeparam name = "T"> Object type </ typeparam>
/// <param name = "source"> value </ param>
/// <param name = "action"> The main function code to be executed on the value </ param>
/// <param name = "failureAction"> Function code in catch </ param>
/// <returns> whether the main function code is executed smoothly </ 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>
/// Execute the specified function on an object, and 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"> the main function code to be executed on the value </ param>
/// <param name = "failureAction"> Function code in catch </ param>
/// <param name = "successAction"> Function code executed after the main function code succeeds </ param>
/// <returns> The return value of the function code, if an exception occurs, the default value of the object type is returned </ 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>
/// Execute the specified function on an object, and 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"> the main function code to be executed on the value </ param>
/// <param name = "failureAction"> Function code in catch </ param>
/// <returns> The return value of the function code, if an exception occurs, the default value of the object type is returned </ 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, finally, but gives some more general methods, mainly because ordinary developers have an understanding of the use of the three blocks, and will not repeat the introduction.
Three. DotNET Exception Class Analysis:
The CLR allows exceptions to throw any type of instance. Here we introduce a System.Exception class:
1.Message attribute: indicates the reason for throwing an 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});
}
}
As can be seen from the above code, Message only has a get attribute, so message is a read-only attribute. GetClassName () gets the class of the exception. GetRuntimeResourceString () Gets the runtime resource string.
2. StackTrace property: Contains the names and signatures of all methods that were called before the exception was 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 state. GetStackTrace () gets the stack trace. Take a look at 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 method for obtaining the stack trace. This method is mainly used by users 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 property is an intrinsic exception, which is a virtual method and is overridden here. Take a closer look at the InnerException property.
[__DynamicallyInvokable]
public Exception InnerException
{
[__DynamicallyInvokable, TargetedPatchingOptOut ("Performance critical to inline this type of method across NGen image boundaries")]
get
{
return this._innerException;
}
}
4.ToString () formats 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, and this.GetClassName () gets information about the exception class.
Above we noticed the [__DynamicallyInvokable] custom property, let's look at the specific implementation code:
[TargetedPatchingOptOut ("Performance critical to inline this type of method across NGen image boundaries")]
public __DynamicallyInvokableAttribute ()
{
}
For the information about the attribute of "Image Boundary" above, please refer to "Via CLR c #", which will not be introduced here.
4. Summary:
In the above introduction to exceptions, the CLR's exception handling mechanism, some of the more common exception codes, and the introduction of the Exception class were mainly introduced. In actual projects, we generally do not throw exceptions directly to customers. When we write programs, we have considered the fault tolerance of the program. After the program catches the exception, try to restore the program or write the exception information to the log. Let the program enter the error page. If a more serious exception occurs, the exception is finally thrown to terminate the program.