Gedanken zur .NET-Ausnahmebehandlung (Teil 1)

黄舟
Freigeben: 2017-02-06 14:43:14
Original
1236 Leute haben es durchsucht

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.

Da für die Implementierung einer Funktion der Kerncode zum Implementieren der Funktion derselbe ist, kann er nur schriftlich optimiert werden, aber in Bezug auf die Klassen, die zum Implementieren einer bestimmten Operation verwendet werden, ist dies die überwiegende Mehrheit Die Zeiten sind gleich.

Es scheint, dass wir im eigentlichen Entwicklungsprozess viele Probleme berücksichtigen müssen, die sich nicht auf die Implementierung einer bestimmten Funktion beschränken, sondern eher auf die Stabilität und Skalierbarkeit des Codes.

Die oben genannten Probleme sind in der tatsächlichen Entwicklung zu bewältigen. Im aktuellen Blog-Beitrag überlegt der Autor auch, wie man diese Ausnahme schreibt und wie man sie versteht Auch verschiedene Gartenfreunde haben ihre eigenen Meinungen zum Schreiben und Umgang mit Ausnahmen geäußert. Hier werde ich einige meiner Verständnisse niederschreiben. Es mag relativ einfach und grob sein, aber es dient nur als Einführung, um das Große anzuleiten Jungs, um über eigene Projekterfahrungen zu sprechen. Ich hoffe, es wird für alle hilfreich sein und jeder ist willkommen, seine eigenen Ideen und Meinungen vorzubringen und sein Wissen und seine Erkenntnisse zu teilen.

1. Überblick über DotNET-Ausnahmen

Wenn es um Ausnahmen geht, müssen wir wissen, was Ausnahmen sind. Wenn wir alles lernen wollen, sollten wir wissen, was wir lernen wollen Es ist gut, ein allgemeines Verständnis im Kopf zu haben. 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, aber Fehler müssen in diesen Konstrukten gemeldet werden , dann muss ein Ausnahmebehandlungsmechanismus bereitgestellt werden.

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 bietet umfangreichere Ausnahmeinformationen; ermöglicht den Thread-Ausstieg am Ende des Catch-Blocks.

Es gibt einige Leitvorschläge zum Umgang mit Ausnahmen.

1. Richtige Verwendung von „finally“-Blöcken

Der „final“-Block kann sicherstellen, dass er unabhängig von der Art der Ausnahme ausgeführt werden kann. Der „final“-Block wird im Allgemeinen zum Bereinigen von Vorgängen verwendet die erfolgreich gestartet wurden, kehren Sie dann zum Aufrufer oder zum Code nach dem „finally“-Block 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);
}
Nach dem Login kopieren

3. Wiederherstellung nach Ausnahmen

Nachdem wir die Ausnahme abgefangen haben, können wir speziell einen Ausnahmewiederherstellungscode schreiben, damit das Programm weiter ausgeführt werden kann. 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 eine Kombination mehrerer Methoden aufrufen, um sie während des Ausführungsprozesses abzuschließen Bei der letztgenannten Methode ist eine Ausnahme aufgetreten. 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 kann es notwendig sein, eine Ausnahme abzufangen und eine andere Ausnahme erneut auszulösen. Dadurch kann der Vertrag der Methode und der ausgelöste Ausnahmetyp beibehalten werden sollte eine bestimmte Ausnahme sein. 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(); 
            } 
            }
Nach dem Login kopieren

Der obige Code veranschaulicht lediglich eine Verarbeitungsmethode. Alle ausgelösten Ausnahmen sollten an den Aufrufstapel der Methode weitergeleitet werden, anstatt sie zu „verschlucken“ und dann eine neue Ausnahme auszulösen. Wenn ein Typkonstruktor eine Ausnahme auslöst und die Ausnahme nicht in der Typkonstruktormethode abgefangen wird, fängt die CLR die Ausnahme intern ab und löst stattdessen eine neue TypeInitialztionException aus.

2. Gemeinsame Behandlungsmechanismen für DotNET-Ausnahmen

在代码发生异常后,我们需要去处理这个异常,如果一个异常没有得到及时的处理,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();
        }
Nach dem Login kopieren

(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&#39;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&#39;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&#39;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&#39;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.");
        }
Nach dem Login kopieren

(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 => { });
        }
Nach dem Login kopieren

本文没有具体介绍try,catch,finally的使用,而是给出一些比较通用的方法,主要是一般的开发者对于三个块的使用都有一个认识,就不再做重复的介绍。

以上就是关于.NET异常处理的思考(上)的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!