>  기사  >  백엔드 개발  >  .NET 예외 처리에 대한 생각(1부)

.NET 예외 처리에 대한 생각(1부)

黄舟
黄舟원래의
2017-02-06 14:43:141231검색

프로젝트 개발에는 시스템과 코드의 안정성과 내결함성에 대한 해당 요구 사항이 있습니다. 실제 개발 프로젝트의 코드와 샘플 코드의 차이점은 코드 작동의 안정성, 내결함성 및 확장성에 관한 것입니다.

함수 구현에 있어서는 함수를 구현하는 핵심 코드가 동일하기 때문에 그냥 서면으로 최적화하면 되지만 특정 연산을 구현하는 데 사용되는 클래스 측면에서는 이것이 압도적으로 많다. 시간은 동일합니다.

실제 개발 과정에서는 특정 기능의 구현에만 국한되지 않고, 코드의 안정성과 확장성에 관한 문제도 많이 고려해야 할 것 같습니다.

위 내용은 실제 개발에서 직면해야 할 문제들입니다. 최근 블로그 게시물에서 저자는 이 예외를 어떻게 작성해야 할지 고민하고 있습니다. 또한 다양한 정원 친구들이 예외 작성 및 처리에 대해 자신의 의견을 제시했습니다. 여기에는 제가 이해한 내용 중 일부를 적어 보겠습니다. 상대적으로 간단하고 거칠 수 있지만 큰 사람들을 이끌기 위한 소개로만 사용됩니다. 실제 프로젝트 경험을 이야기합니다. 이 글이 모든 사람에게 도움이 되기를 바라며, 누구나 자신의 생각과 의견을 제시하고 지식과 통찰력을 공유할 수 있기를 바랍니다.

1. DotNET 예외 개요

예외에 관해서는 모든 것을 배우고 싶다면 무엇을 배우고 싶은지 알아야 합니다. 마음 속으로 대략적인 이해를 갖는 것이 좋습니다. 구성원이 이름에 명시된 작업을 수행하지 못하는 경우 예외가 발생합니다.

.NET에서 생성자, 속성 가져오기 및 설정, 이벤트 추가 및 삭제, 연산자 오버로드 호출, 변환 연산자 호출 등은 오류 코드를 반환할 방법이 없지만 이러한 구문에서 오류를 보고해야 합니다. , 예외 처리 메커니즘을 제공해야 합니다.

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

예외 처리 메커니즘에는 일반적으로 세 가지 옵션이 있습니다. 동일한 예외를 다시 발생시키고 호출 스택에서 한 수준 더 높은 코드에 예외 발생을 알리는 것입니다. 호출 스택 한 계층의 코드는 더 풍부한 예외 정보를 제공하여 스레드가 catch 블록의 맨 아래에서 종료되도록 합니다.

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

1. finally 블록의 올바른 사용

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

2. 예외 캡처가 적절해야 합니다

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

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

catch 블록에서는 System.Exception을 사용하여 예외를 catch할 수 있지만 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. 계약을 유지하기 위해 구현 세부 정보를 숨깁니다.

경우에 따라 예외를 포착하고 다른 예외를 다시 발생시켜야 할 수도 있습니다. 이렇게 하면 메서드의 계약이 유지될 수 있으며 발생된 예외 유형은 다음과 같습니다. be는 특별한 예외입니다. 다음 코드를 보세요:

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

위 코드는 단지 처리 방법을 보여줍니다. 발생하는 모든 예외는 해당 예외를 "삼키고" 새 예외를 발생시키는 대신 메서드의 호출 스택으로 전달되어야 합니다. 형식 생성자가 예외를 throw하고 해당 예외가 형식 생성자 메서드에서 포착되지 않는 경우 CLR은 내부적으로 예외를 포착하고 대신 새 TypeInitialztionException을 발생시킵니다.

2. 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)!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.