Maison >développement back-end >Tutoriel C#.Net >Analyser la gestion des exceptions .NET

Analyser la gestion des exceptions .NET

巴扎黑
巴扎黑original
2017-08-16 17:06:191645parcourir

Cet article présente principalement un résumé des réflexions sur la gestion des exceptions .NET, qui a une certaine valeur de référence. Les amis intéressés peuvent s'y référer.

La nouvelle année approche. Pour la plupart des programmeurs, il sera temps de prendre un peu de temps libre. Cependant, pendant ce temps libre, la seule façon de passer le temps est de se demander quel langage est le meilleur. On estime que ce sera le cas récemment. Il existe de nombreux articles de blog sur Java et .net. J'ai dit qu'en tant que mangeur de melon, je regarderais tranquillement les grands exprimer leurs sentiments.

Assez de bêtises ci-dessus, je n'arrêterai pas de dire des bêtises ici, allons droit au but.

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, et il peut simplement être optimisé en termes d'écriture, mais en termes de classes utilisées pour implémenter une certaine opération, c'est la même chose la plupart du temps. De ce point de vue, nous devons prendre en compte de nombreux problèmes au cours du processus de développement lui-même, qui ne se limitent pas à la mise en œuvre d'une fonction spécifique, mais tiennent également compte de la stabilité et de 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 compréhension de l'exception. tout le monde, vous êtes également invités à proposer vos propres idées et opinions, et à partager vos connaissances et vos idées.

1. Aperçu des exceptions DotNET :

En ce qui concerne les exceptions, nous devons savoir ce que sont les exceptions, et tout si nous le voulons. pour apprendre, nous devons savoir ce que nous voulons apprendre, afin d'avoir une compréhension générale dans notre cœur. 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, qui obtiennent et définissent des propriétés, ajoutent et suppriment des événements, appellent des surcharges d'opérateurs et appellent des opérateurs de conversion, etc., n'ont aucun moyen de renvoyer des codes d'erreur. Cependant, si des erreurs doivent être signalées dans ces constructions, des exceptions doivent être faites. mécanisme de traitement 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. Utilisez le bloc final de manière appropriée :

Le bloc final peut garantir que quel que soit le type d'exception levée par le thread, il peut être exécuté. Le bloc final est généralement utilisé pour le nettoyage. Les opérations qui ont été lancées avec succès reviendront 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, parce que nous ne pouvons pas intercepter toutes les exceptions, après avoir intercepté les exceptions, nous devons gérer ces exceptions, mais si nous 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 des exceptions ciblées Le code restauré permet le programme pour continuer à fonctionner. 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 plusieurs combinaisons de méthodes pour la terminer. Il semble que les premières méthodes soient terminées, mais des exceptions se produisent dans les méthodes suivantes. 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, afin que le contrat de la méthode puisse être maintenue. Le type d’anomalie cardiaque doit être une anomalie 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(); 
   } 
   }

以上的代码只是在说明一种处理方式。应该让抛出的所有异常都沿着方法的调用栈向上传递,而不是把他们”吞噬“了之后抛出一个新的异常。如果一个类型构造器抛出一个异常,而且该异常未在类型构造器方法中捕获,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&#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的使用,而是给出一些比较通用的方法,主要是一般的开发者对于三个块的使用都有一个认识,就不再做重复的介绍。

三.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类的介绍。在实际的项目中,我们一般不要将异常直接抛出给客户,我们在编写程序时,已经考虑程序的容错性,在程序捕获到异常后,尽量去恢复程序,或者将异常信息写入日志,让程序进入错误页。如果出现比较严重的异常,最后将异常抛出,终止程序。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

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