Maison  >  Article  >  développement back-end  >  Réflexions sur la gestion des exceptions .NET (Partie 1)

Réflexions sur la gestion des exceptions .NET (Partie 1)

黄舟
黄舟original
2017-02-06 14:43:141237parcourir

Dans le développement de projets, il existe des exigences correspondantes en matière de stabilité et de tolérance aux pannes du système et du code. La différence entre le code des projets de développement réels et l'exemple de code concerne davantage la stabilité, la tolérance aux pannes et l'évolutivité du fonctionnement du code.

Parce que pour implémenter une fonction, le code de base pour implémenter la fonction est le même, il peut simplement être optimisé par écriture, mais en termes de classes utilisées pour implémenter une certaine opération, c'est la grande majorité. les temps sont les mêmes.

Il semble que dans le processus de développement lui-même, nous devions prendre en compte de nombreux problèmes, qui ne se limitent pas à la mise en œuvre d'une fonction spécifique, mais plutôt à la stabilité et à l'évolutivité du code.

Ce qui précède sont les problèmes auxquels il faut faire face dans le développement réel. Dans le récent article de blog, l'auteur réfléchit également à la manière d'écrire cette exception et à la façon de comprendre l'exception. aussi divers Quelques amis jardiniers ont exprimé leurs propres opinions sur l'écriture et la gestion des exceptions. Ici, je vais écrire une partie de ma compréhension. Cela peut être relativement simple et approximatif, mais cela n'est utilisé que comme introduction pour diriger le grand. les gars pour parler. J'espère que cela sera utile à tout le monde et que chacun est invité à proposer ses propres idées et opinions, et à partager ses connaissances et ses idées.

1. Présentation des exceptions DotNET

En ce qui concerne les exceptions, nous devons savoir ce que sont les exceptions. Si nous voulons tout apprendre, nous devons savoir ce que nous voulons apprendre, c'est donc. c'est bien d'avoir une compréhension générale dans votre esprit. Une exception se produit lorsqu'un membre ne parvient pas à effectuer l'action que son nom indique qu'il peut effectuer.

Dans .NET, les constructeurs, obtenant et définissant des propriétés, ajoutant et supprimant des événements, appelant des surcharges d'opérateurs et appelant des opérateurs de conversion, etc., n'ont aucun moyen de renvoyer des codes d'erreur, mais les erreurs doivent être signalées dans ces constructions. , alors un mécanisme de gestion des exceptions doit être fourni.

Dans la gestion des exceptions, les trois blocs que nous utilisons souvent sont : try block ; catch block then block ; Ces trois blocs peuvent être utilisés ensemble ou sans bloc catch. Les blocs de gestion des exceptions peuvent être utilisés imbriqués. La méthode spécifique sera présentée ci-dessous.

Dans le mécanisme de gestion des exceptions, il existe généralement trois options : relancer la même exception et notifier le code un niveau plus haut dans la pile d'appels de l'occurrence de l'exception et notifier le code plus haut dans la pile d'appels ; la pile d'appels Une couche de code fournit des informations d'exception plus riches ; permet au thread de sortir du bas du bloc catch.

Il existe quelques suggestions directrices sur la façon de gérer les exceptions.

1. Utilisation appropriée des blocs final

Le bloc final peut garantir que quel que soit le type d'exception levée par le thread, il peut être exécuté. qui ont été démarrés avec succès. Revenez ensuite à l'appelant ou au code après le bloc final.

2. La capture des exceptions doit être appropriée

Pourquoi les exceptions doivent-elles être capturées de manière appropriée ? Le code suivant est dû au fait que nous ne pouvons pas détecter toutes les exceptions. Après avoir détecté les exceptions, nous devons gérer ces exceptions. Si nous captons toutes les exceptions mais ne prévoyons pas les exceptions qui se produiront, nous n'aurons aucun moyen de gérer ces exceptions.

Si le code de l'application lève une exception, l'autre extrémité de l'application peut s'attendre à intercepter cette exception, elle ne peut donc pas être écrite comme un bloc d'exception "de taille universelle" et l'exception doit être autorisée à remontez la pile d'appels et laissez le code de l'application gérer spécifiquement cette exception.

Dans le bloc catch, vous pouvez utiliser System.Exception pour intercepter les exceptions, mais il est préférable de renvoyer l'exception à la fin du bloc catch. La raison sera expliquée plus tard.

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. Récupérer des exceptions

Après avoir détecté l'exception, nous pouvons écrire du code de récupération d'exception spécifiquement pour permettre au programme de continuer à s'exécuter. Lors de la détection d'exceptions, vous devez détecter des exceptions spécifiques, bien comprendre dans quelles circonstances une exception sera levée et savoir quels types sont dérivés du type d'exception capturé. Ne gérez pas et n’interceptez pas les exceptions System.Exception à moins qu’elles ne soient renvoyées à la fin du bloc catch.

4. Maintenir l'état

Généralement, lorsque nous terminons une opération ou une méthode, nous devons appeler une combinaison de plusieurs méthodes pour la terminer. Pendant le processus d'exécution, l'achèvement de la précédente. méthodes apparaîtront. Une exception s’est produite dans cette dernière méthode. Lorsqu'une exception irrécupérable se produit, l'opération partiellement terminée est annulée, car nous devons restaurer les informations. Ainsi, lorsque nous interceptons l'exception, nous devons capturer toutes les informations sur l'exception.

5. Masquer les détails d'implémentation pour conserver le contrat

Parfois, vous devrez peut-être intercepter une exception et renvoyer une exception différente. Cela peut maintenir le contrat de la méthode, et le type d'exception généré devrait le faire. be est une exception spécifique. Regardez le code suivant :

FileStream fs = null;
            try
            {
                fs = FileStream();
              
            }
            catch (FileNotFoundException e)
            {
          //抛出一个不同的异常,将异常信息包含在其中,并将原来的异常设置为内部异常
                throw new NameNotFoundException();
            }
            catch (IOException e)
            {
 
               //抛出一个不同的异常,将异常信息包含在其中,并将原来的异常设置为内部异常
             throw new NameNotFoundException(); 
            } 
            finally 
            {
               if (fs != null) 
                { 
               fs.close(); 
            } 
            }

Le code ci-dessus illustre simplement une méthode de traitement. Toutes les exceptions levées doivent être transmises dans la pile d'appels de la méthode, plutôt que de les "avaler" puis de lancer une nouvelle exception. Si un constructeur de type lève une exception et que l'exception n'est pas interceptée dans la méthode du constructeur de type, le CLR interceptera l'exception en interne et lancera une nouvelle TypeInitialztionException à la place.

2. Mécanismes de gestion courants pour les exceptions 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&#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.");
        }

(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的使用,而是给出一些比较通用的方法,主要是一般的开发者对于三个块的使用都有一个认识,就不再做重复的介绍。

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


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn