Dieser Artikel stellt hauptsächlich eine Zusammenfassung der Gedanken zur .NET-Ausnahmebehandlung dar, die einen gewissen Referenzwert hat.
Das neue Jahr rückt näher. Für die meisten Programmierer ist es jedoch an der Zeit, sich eine Auszeit zu nehmen, indem sie darüber diskutieren, welche Sprache besser ist Es wird geschätzt, dass dies in letzter Zeit der Fall sein wird. Es gibt viele Blogbeiträge über Java und .net. Ich habe gesagt, dass ich als Melonenfresser ruhig zusehen würde, wie die Großen ihre Gefühle ausdrücken.
Genug des obigen Unsinns, ich werde hier nicht aufhören, Unsinn zu reden, kommen wir zum Punkt.
In der Projektentwicklung gibt es entsprechende Anforderungen an die Stabilität und Fehlertoleranz des Systems und Codes. Der Unterschied zwischen dem Code in tatsächlichen Entwicklungsprojekten und dem Beispielcode liegt eher in der Stabilität, Fehlertoleranz und Skalierbarkeit der Codeoperationen. Denn für die Implementierung einer Funktion ist der Kerncode zur Implementierung der Funktion derselbe und kann nur in Bezug auf das Schreiben optimiert werden. In Bezug auf die Klassen, die zur Implementierung einer bestimmten Operation verwendet werden, ist dies jedoch meistens derselbe. Unter diesem Gesichtspunkt müssen wir während des eigentlichen Entwicklungsprozesses viele Aspekte berücksichtigen, die sich nicht nur auf die Implementierung einer bestimmten Funktion beschränken, sondern auch die Stabilität und Skalierbarkeit des Codes berücksichtigen.
Die oben genannten Probleme sind in der tatsächlichen Entwicklung zu bewältigen. In dem aktuellen Blog-Beitrag überlegt der Autor auch, wie man diese Ausnahme schreibt und wie man sie versteht Gerne können Sie auch Ihre eigenen Ideen und Meinungen einbringen und Ihr Wissen und Ihre Erkenntnisse teilen.
1. Übersicht über DotNET-Ausnahmen:
Wenn es um Ausnahmen geht, müssen wir wissen, was Ausnahmen sind, und zwar alles, wenn wir wollen Um zu lernen, sollten wir wissen, was wir lernen wollen, damit wir ein allgemeines Verständnis in unserem Herzen haben können. Eine Ausnahme tritt auf, wenn ein Mitglied die Aktion nicht ausführt, die laut Name des Mitglieds möglich ist. In .NET haben Konstruktoren, das Abrufen und Festlegen von Eigenschaften, das Hinzufügen und Löschen von Ereignissen, das Aufrufen von Operatorüberladungen und das Aufrufen von Konvertierungsoperatoren usw. keine Möglichkeit, Fehlercodes zurückzugeben. Wenn in diesen Konstrukten jedoch Fehler gemeldet werden müssen, müssen Ausnahmen erfolgen Verarbeitungsmechanismus bereitgestellt.
Bei der Ausnahmebehandlung verwenden wir häufig die folgenden drei Blöcke: try-Block; Diese drei Blöcke können zusammen oder ohne Ausnahmebehandlungsblöcke verschachtelt verwendet werden. Die spezifische Methode wird unten vorgestellt.
Im Ausnahmebehandlungsmechanismus gibt es im Allgemeinen drei Optionen: Dieselbe Ausnahme erneut auslösen und den Code eine Ebene höher im Aufrufstapel über das Auftreten der Ausnahme benachrichtigen Der Aufrufstapel Eine Codeebene stellt umfangreichere Ausnahmeinformationen bereit; ermöglicht den Thread-Ausstieg am Ende des Catch-Blocks.
Es gibt einige Leitvorschläge zum Umgang mit Ausnahmen.
1. Verwenden Sie den „finally“-Block entsprechend:
Der „final“-Block kann sicherstellen, dass der letzte Block unabhängig von der Art der Ausnahme ausgeführt werden kann Wird im Allgemeinen zur Bereinigung verwendet. Die erfolgreich initiierten Vorgänge kehren dann nach dem „finally“-Block zum Aufrufer oder zum Code zurück.
2. Ausnahmeerfassung muss angemessen sein:
Warum sollten Ausnahmen angemessen erfasst werden? Der folgende Code liegt daran, dass wir nicht alle Ausnahmen abfangen können. Wenn wir alle Ausnahmen abfangen, aber die auftretenden Ausnahmen nicht vorhersehen können, haben wir keine Möglichkeit, diese Ausnahmen zu behandeln.
Wenn der Anwendungscode eine Ausnahme auslöst, erwartet das andere Ende der Anwendung möglicherweise, diese Ausnahme abzufangen. Daher kann sie nicht als „Größe-passt-alle“-Ausnahmeblock geschrieben werden und die Ausnahme sollte dies zulassen Verschieben Sie den Aufrufstapel nach oben und lassen Sie den Anwendungscode diese Ausnahme speziell behandeln.
Im Catch-Block können Sie System.Exception verwenden, um Ausnahmen abzufangen. Es ist jedoch besser, die Ausnahme am Ende des Catch-Blocks erneut auszulösen. Der Grund wird später erläutert.
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("创建注册表错误" + ex); throw new Exception(ex.Message,ex); }
3. Wiederherstellung nach Ausnahmen:
Nachdem wir die Ausnahme abgefangen haben, können wir einige gezielte Ausnahmen schreiben, die der wiederhergestellte Code zulässt das Programm weiterlaufen zu lassen. Beim Abfangen von Ausnahmen müssen Sie bestimmte Ausnahmen abfangen, vollständig verstehen, unter welchen Umständen eine Ausnahme ausgelöst wird, und wissen, welche Typen vom Typ der abgefangenen Ausnahme abgeleitet werden. Behandeln oder fangen Sie keine System.Exception-Ausnahmen ab, es sei denn, sie werden am Ende des Catch-Blocks erneut ausgelöst.
4. Status beibehalten:
Wenn wir eine Operation oder eine Methode abschließen, müssen wir im Allgemeinen mehrere Methodenkombinationen aufrufen, um sie während des Ausführungsprozesses abzuschließen. Es scheint, dass die ersten paar Methoden abgeschlossen sind, bei den folgenden Methoden treten jedoch Ausnahmen auf. Wenn eine nicht behebbare Ausnahme auftritt, wird der teilweise abgeschlossene Vorgang zurückgesetzt, da wir die Informationen wiederherstellen müssen. Wenn wir also die Ausnahme abfangen, müssen wir alle Ausnahmeinformationen erfassen.
5. Implementierungsdetails ausblenden, um den Vertrag aufrechtzuerhalten:
Manchmal müssen Sie möglicherweise eine Ausnahme abfangen und eine andere Ausnahme erneut auslösen, damit der Vertrag der Methode funktionieren kann Bei der Art der Herzanomalie sollte es sich um eine spezifische Anomalie handeln. Schauen Sie sich den folgenden Code an:
FileStream fs = null; try { fs = FileStream(); } catch (FileNotFoundException e) { //抛出一个不同的异常,将异常信息包含在其中,并将原来的异常设置为内部异常 throw new NameNotFoundException(); } catch (IOException e) { //抛出一个不同的异常,将异常信息包含在其中,并将原来的异常设置为内部异常 throw new NameNotFoundException(); } finally { if (fs != null) { fs.close(); } }
以上的代码只是在说明一种处理方式。应该让抛出的所有异常都沿着方法的调用栈向上传递,而不是把他们”吞噬“了之后抛出一个新的异常。如果一个类型构造器抛出一个异常,而且该异常未在类型构造器方法中捕获,CLR就会在内部捕获该异常,并改为抛出一个新的TypeInitialztionException。
二.DotNET异常的常用处理机制:
在代码发生异常后,我们需要去处理这个异常,如果一个异常没有得到及时的处理,CLR会终止进程。在异常的处理中,我们可以在一个线程捕获异常,在另一个线程中重新抛出异常。异常抛出时,CLR会在调用栈中向上查找与抛出的异常类型匹配的catch块。如果没有任何catch块匹配抛出的异常类型,就发生一个未处理异常。CLR检测到进程中的任何线程有一个位处理异常,都会终止进程。
1.异常处理块:
(1).try块:包含代码通常需要执行一些通用的资源清理操作,或者需要从异常中恢复,或者两者都需要。try块还可以包含也许会抛出异常的代码。一个try块至少有一个关联的catch块或finall块。
(2).catch块:包含的是响应一个异常需要执行的代码。catch关键字后的圆括号中的表达式是捕获类型。捕获类型从System.Exception或者其派生类指定。CLR自上而下搜素一个匹配的catch块,所以应该教具体的异常放在顶部。一旦CLR找到一个具有匹配捕获类型的catch块,就会执行内层所有finally块中的代码,”内层finally“是指抛出异常的tey块开始,到匹配异常的catch块之间的所有finally块。
使用System.Exception捕捉异常后,可以采用在catch块的末尾重新抛出异常,因为如果我们在捕获Exception异常后,没有及时的处理或者终止程序,这一异常可能对程序造成很大的安全隐患,Exception类是所有异常的基类,可以捕获程序中所有的异常,如果出现较大的异常,我们没有及时的处理,造成的问题是巨大的。
(3).finally块:包含的代码是保证会执行的代码。finally块的所有代码执行完毕后,线程退出finally块,执行紧跟在finally块之后的语句。如果不存在finally块,线程将从最后一个catch块之后的语句开始执行。
备注:异常块可以组合和嵌套,对于三个异常块的样例,在这里就不做介绍,异常的嵌套可以防止在处理异常的时候再次出现未处理的异常,以上这些就不再赘述。
2.异常处理实例:
(1).异常处理扩展方法:
/// <summary> /// 格式化异常消息 /// </summary> /// <param name="e">异常对象</param> /// <param name="isHideStackTrace">是否隐藏异常规模信息</param> /// <returns>格式化后的异常信息字符串</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}异常消息:{1}", appString, e.Message)); sb.AppendLine(string.Format("{0}异常类型:{1}", appString, e.GetType().FullName)); sb.AppendLine(string.Format("{0}异常方法:{1}", appString, (e.TargetSite == null ? null : e.TargetSite.Name))); sb.AppendLine(string.Format("{0}异常源:{1}", appString, e.Source)); if (!isHideStackTrace && e.StackTrace != null) { sb.AppendLine(string.Format("{0}异常堆栈:{1}", appString, e.StackTrace)); } if (e.InnerException != null) { sb.AppendLine(string.Format("{0}内部异常:", appString)); count++; } e = e.InnerException; } return sb.ToString(); }
(2).验证异常:
/// <summary> /// 检查字符串是空的或空的,并抛出一个异常 /// </summary> /// <param name="val">值测试</param> /// <param name="paramName">参数检查名称</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> /// 请检查参数不是空的或空的,并抛出异常 /// </summary> /// <param name="param">检查值</param> /// <param name="paramName">参数名称</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> /// 检查参数不是无效,并抛出一个异常 /// </summary> /// <param name="param">检查值</param> /// <param name="paramName">参数名称</param> public static void CheckNullParam(object param, string paramName) { if (param == null) throw new ArgumentNullException(paramName, paramName + " can't be null"); } /// <summary> /// 请检查参数1不同于参数2 /// </summary> /// <param name="param1">值1测试</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> /// 检查一个整数值是正的(0或更大) /// </summary> /// <param name="val">整数测试</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扩展操作:
/// <summary> /// 对某对象执行指定功能与后续功能,并处理异常情况 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="source">值</param> /// <param name="action">要对值执行的主功能代码</param> /// <param name="failureAction">catch中的功能代码</param> /// <param name="successAction">主功能代码成功后执行的功能代码</param> /// <returns>主功能代码是否顺利执行</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> /// 对某对象执行指定功能,并处理异常情况 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="source">值</param> /// <param name="action">要对值执行的主功能代码</param> /// <param name="failureAction">catch中的功能代码</param> /// <returns>主功能代码是否顺利执行</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> /// 对某对象执行指定功能,并处理异常情况与返回值 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <typeparam name="TResult">返回值类型</typeparam> /// <param name="source">值</param> /// <param name="func">要对值执行的主功能代码</param> /// <param name="failureAction">catch中的功能代码</param> /// <param name="successAction">主功能代码成功后执行的功能代码</param> /// <returns>功能代码的返回值,如果出现异常,则返回对象类型的默认值</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> /// 对某对象执行指定功能,并处理异常情况与返回值 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <typeparam name="TResult">返回值类型</typeparam> /// <param name="source">值</param> /// <param name="func">要对值执行的主功能代码</param> /// <param name="failureAction">catch中的功能代码</param> /// <returns>功能代码的返回值,如果出现异常,则返回对象类型的默认值</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 => { }); }
本文没有具体介绍try,catch,finally的使用,而是给出一些比较通用的方法,主要是一般的开发者对于三个块的使用都有一个认识,就不再做重复的介绍。
三.DotNET的Exception类分析:
CLR允许异常抛出任何类型的实例,这里我们介绍一个System.Exception类:
1.Message属性:指出抛出异常的原因。
[__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 }); } }
由以上的代码可以看出,Message只具有get属性,所以message是只读属性。GetClassName()获取异常的类。GetRuntimeResourceString()获取运行时资源字符串。
2.StackTrace属性:包含抛出异常之前调用过的所有方法的名称和签名。
public static string StackTrace { [SecuritySafeCritical] get { new EnvironmentPermission(PermissionState.Unrestricted).Demand(); return GetStackTrace(null, true); } }
EnvironmentPermission()用于环境限制,PermissionState.Unrestricted设置权限状态,GetStackTrace()获取堆栈跟踪,具体看一下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); }
以上是获取堆栈跟踪方法的具体实现,此方法主要用户调试的时候。
3.GetBaseException()获取基础异常信息方法。
[__DynamicallyInvokable] public virtual Exception GetBaseException() { Exception innerException = this.InnerException; Exception exception2 = this; while (innerException != null) { exception2 = innerException; innerException = innerException.InnerException; } return exception2; }
InnerException属性是内在异常,这是一个虚方法,在这里被重写。具体看一下InnerException属性。
[__DynamicallyInvokable] public Exception InnerException { [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get { return this._innerException; } }
4.ToString()将异常信息格式化。
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; }
在此方法中,将获取的异常信息进行格式化为字符串,this.GetClassName() 获取异常类的相关信息。
以上我们注意到[__DynamicallyInvokable]定制属性,我们看一下具体的实现代码:
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public __DynamicallyInvokableAttribute() { }
以上我们主要注释部分,”图像边界“这个属性的相关信息,请参见《Via CLR c#》,这里就不做具体的介绍。
四.总结:
以上在对异常的介绍中,主要介绍了CLR的异常处理机制,一些较为通用的异常代码,以及对Exception类的介绍。在实际的项目中,我们一般不要将异常直接抛出给客户,我们在编写程序时,已经考虑程序的容错性,在程序捕获到异常后,尽量去恢复程序,或者将异常信息写入日志,让程序进入错误页。如果出现比较严重的异常,最后将异常抛出,终止程序。
Das obige ist der detaillierte Inhalt vonAnalysieren Sie die .NET-Ausnahmebehandlung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!