> 백엔드 개발 > C#.Net 튜토리얼 > .NET 예외 처리 분석

.NET 예외 처리 분석

巴扎黑
풀어 주다: 2017-08-16 17:06:19
원래의
1650명이 탐색했습니다.

이 글은 관심 있는 친구들이 참고할 수 있는 특정 참고 가치가 있는 .NET 예외 처리에 대한 생각을 요약한 내용을 주로 소개합니다.

새해가 다가오고 있어 대부분의 프로그래머들에게는 잠시 쉬어가는 시간이겠지만, 이 여유시간에는 어떤 언어가 더 나은지 논쟁을 벌이는 것이 유일한 방법일 것으로 추정됩니다. 최근 자바에 대한 이야기가 많이 나오네요. .net 블로그 글이 올라왔을 때, 저는 멜론을 먹는 사람으로서 대기업들이 그들의 감정을 표현하는 것을 조용히 지켜본다고 말했습니다.

위에서 말도 안되는 얘기는 충분히 했으니 여기서는 말도 안되는 얘기는 하지 않고 본론으로 들어가겠습니다.

프로젝트 개발에는 시스템과 코드의 안정성과 내결함성에 대한 해당 요구 사항이 있습니다. 실제 개발 프로젝트의 코드와 샘플 코드의 차이점은 코드 작동의 안정성, 내결함성 및 확장성에 관한 것입니다. 함수를 구현하는 데 있어서 함수를 구현하는 핵심 코드는 동일하기 때문에 작성 측면에서는 최적화할 수도 있지만 특정 작업을 구현하는 데 사용되는 클래스 측면에서는 대부분 동일합니다. 이러한 관점에서 우리는 실제 개발 과정에서 많은 문제를 고려해야 하는데, 이는 특정 기능의 구현에만 국한되지 않고 코드의 안정성과 확장성도 고려해야 합니다.

위 내용은 실제 개발에서 직면해야 할 문제들입니다. 최근 블로그 게시물에서 저자는 이 예외를 어떻게 작성하고, 어떻게 이해해야 하는지 고민하고 있으며, 모든 분들께 도움이 되기를 바랍니다. 누구나 자신만의 아이디어와 의견을 제시하고 지식과 통찰력을 공유할 수 있습니다.

1. DotNET 예외 개요:

예외에 관해서는 모든 것을 배우고 싶다면 무엇을 배우고 싶은지 알아야 합니다. 전반적인 이해를 하시면 좋습니다. 구성원이 이름에 명시된 작업을 수행하지 못하는 경우 예외가 발생합니다. .NET에서 생성자, 속성 가져오기 및 설정, 이벤트 추가 및 삭제, 연산자 오버로드 호출, 변환 연산자 호출 등은 오류 코드를 반환할 수 있는 방법이 없습니다. 그러나 이러한 구문에서 오류를 보고해야 하는 경우 예외가 발생해야 합니다. 처리 메커니즘을 제공합니다.

예외 처리에서 우리가 자주 사용하는 세 가지 블록은 try 블록, finally 블록입니다. 이 세 블록은 함께 사용할 수도 있고 catch 블록 없이 사용할 수도 있습니다. 구체적인 방법은 아래에 설명되어 있습니다.

예외 처리 메커니즘에는 일반적으로 세 가지 옵션이 있습니다. 동일한 예외를 다시 발생시키고 호출 스택의 상위 수준에 있는 코드에 예외가 발생했음을 알리고 다른 예외를 발생시키고 이를 상위 수준의 코드에 제공합니다. 호출 스택이 더 풍부해졌습니다. 스레드가 catch 블록의 맨 아래에서 종료되도록 합니다.

예외 처리 방법에 대한 몇 가지 지침 제안이 있습니다.

1. finally 블록의 올바른 사용:

final 블록은 스레드가 어떤 유형의 예외를 발생시키더라도 일반적으로 성공적으로 시작된 작업을 정리하는 데 사용됩니다. , 그리고 나서 호출자에게 돌아갑니다. 또는 finally 블록 뒤의 코드입니다.

2. 예외 포착이 적절해야 합니다:

예외를 적절하게 포착해야 하는 이유는 무엇인가요? 다음 코드는 모든 예외를 포착할 수 없기 때문에 예외를 포착한 후에 이러한 예외를 처리해야 합니다. 모든 예외를 포착했지만 발생할 예외를 예측하지 못하면 이러한 예외를 처리할 방법이 없습니다.

애플리케이션 코드에서 예외가 발생하면 애플리케이션의 다른 쪽 끝에서 이 예외를 포착할 것으로 예상할 수 있으므로 "모든 크기에 맞는" 예외 블록으로 작성할 수 없습니다. 예외는 호출을 위로 이동하도록 허용되어야 합니다. 프로그램 코드는 이 예외를 구체적으로 처리합니다.

catch 블록에서는 System.Exception을 사용하여 예외를 잡을 수 있지만 catch 블록 끝에서 예외를 다시 발생시키는 것이 더 좋습니다. 이유는 나중에 설명하겠습니다.


   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. 예외 복구:

예외를 포착한 후 프로그램이 계속 실행될 수 있도록 특별히 몇 가지 예외 복구 코드를 작성할 수 있습니다. 예외를 포착할 때는 특정 예외를 포착하고, 어떤 상황에서 예외가 발생하는지 완전히 이해하고, 포착된 예외 유형에서 어떤 유형이 파생되는지 알아야 합니다. catch 블록 끝에서 다시 발생하지 않는 한 System.Exception 예외를 처리하거나 catch하지 마세요.

4. 상태 유지:

일반적으로 작업이나 메서드를 완료하면 이를 완료하기 위해 여러 메서드의 조합을 호출해야 합니다. 이후 방법에서는 예외가 발생합니다. 복구할 수 없는 예외가 발생하면 부분적으로 완료된 작업이 롤백됩니다. 정보를 복원해야 하기 때문입니다. 따라서 예외를 포착할 때 모든 예외 정보를 캡처해야 합니다.

5. 계약을 유지하기 위해 구현 세부 정보 숨기기:

경우에 따라 예외를 포착하고 다른 예외를 다시 발생시켜야 할 수 있습니다. 이렇게 하면 발생한 예외 유형이 특정 예외여야 합니다. . 다음 코드를 보세요:


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

위 내용은 .NET 예외 처리 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿