C# 异常处理

C#中的异常处理原则

前言:写代码写的多了,越来越觉得异常处理的重要性,在出现异常的时候一是能保证程序不至于崩溃,给客户一个黄页或者windows的停止响应,二是迅速定位到错误的位置,因为很多时候,一个完整的多人合作的项目,也不是很方便去调试,就比如最近在做的sharepoint网站的项目,一次调试不仅要占用开发机的IIS资源,而且还需要很长的时间,效率是非常低的。而完善的异常处理和日志,能救人于水火之中啊,闲话少说,下面进入主题
ps:写这篇的时候,资料不在手头,很痛苦。。。

  • 异常类exception的内部结构已经了解了
  • 系统级异常和应用程序级异常的区别
  • 自定义异常类最佳实践原则
  • java中异常处理的原则
  • //log4net使用指南

0.1 错误、bug和异常

这个概念也是最近才有的,其实我们平时说的bug和异常,其实是两个东西。

  • bug:简单来说,是由程序员引起的错误,比如超出索引。
  • 用户错误:和bug不同,用户错误往往不是由应用程序作者,而是由运行程序的用户引起的错误。例如代码中没有处理错误输入,而用户在文本框中输入了格式非法的字符串时,会产生用户错误。
  • 异常:异常往往是运行时的非正常情况,在编程时很难被估计到。异常可能包括:链接一个不存在的数据库,打开已经被破坏的xml文件。在上述各种情况下,程序员和最终用户都无法完全控制这些异常情况。

1.1 异常的基类机构和各个属性的定义

异常的基类结构如下

namespace System
{
    //
    // 摘要:
    //     表示在应用程序执行过程中发生的错误。
    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(_Exception))]
    [ComVisible(true)]
    public class Exception : ISerializable, _Exception
    {
       
        public Exception();    
        public Exception(string message);    
        public Exception(string message, Exception innerException);


        [SecuritySafeCritical]
        protected Exception(SerializationInfo info, StreamingContext context);


        //     获取一个提供用户定义的其他异常信息的键/值对的集合。
        public virtual IDictionary Data { get; }
        public virtual string HelpLink { get; set; }

        //     获取或设置 HRESULT(一个分配给特定异常的编码数字值)。
        public int HResult { get; protected set; }

        //     一个 Exception 的实例,描述导致当前异常的错误。InnerException 属性返回与传递给构造函数的值相同的值,或者,如果没有向构造函数提供内部异常值,则返回
        public Exception InnerException { get; }
        public virtual string Message { get; }

        //     导致错误的应用程序或对象的名称。
        public virtual string Source { get; set; }

        //     获取调用堆栈上直接帧的字符串表示形式。
        public virtual string StackTrace { get; }

        //     获取引发当前异常的方法。
        public MethodBase TargetSite { get; }

        //     在序列化异常,以创建包含有关异常的序列化数据的异常状态对象时发生。
        protected event EventHandler<SafeSerializationEventArgs> SerializeObjectState;


        //     异常链中第一个被引发的异常。如果当前异常的 System.Exception.InnerException 属性是 null 引用(Visual Basic
        //     中为 Nothing),则此属性返回当前异常。
        public virtual Exception GetBaseException();

        //     当在派生类中重写时,用关于异常的信息设置 System.Runtime.Serialization.SerializationInfo。
        [SecurityCritical]
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context);

        public Type GetType();
        public override string ToString();
    }
}

在构建自定义异常处理类的时候,使用exception的代码段可以自动生成自定义异常处理类的结构。并且,仅在出现错误的类与该错误关系紧密时,才需要创建自定义的异常。

ps:在捕获异常时候,使用try;catch;finally,throw;四个关键字来构架你自己的异常处理流程。

2.1 异常处理的原则

学习了一个Java的博客,总结的很好,异常处理的原则有三个:

  • 具体明确
  • 提早抛出
  • 延迟捕获

具体明确

比如具体的业务场景读取文件,可能会出现文件找不到FileNotFoundException,或者文件名太长PathTooLongException等等许多不同类型的exception,而且不同的exception的处理方式不同,比如找不到文件可能需要用户再次指定准确的路径,文件名太长让用户修改文件名之后重试等等,因为一个try是允许多个catch块来处理的,越是捕获到明确的异常,越是方便后续的处理。

提早抛出

异常堆栈信息提供了导致异常出现的方法调用链的精确顺序,包括每个方法调用的类名,方法名,代码文件名甚至行数,以此来精确定位异常出现的现场。通过提早抛出异常(又称"迅速失败"),异常得以清晰又准确。堆栈信息立即反映出什么出了错(提供了非法参数值),为什么出错(文件名不能为空值),以及哪里出的错

另外,其中包含的异常信息("文件名为空")通过明确回答什么为空这一问题使得异常提供的信息更加丰富,而这一答案是我们之前代码中抛出的NullPointerException所无法提供的。
通过在检测到错误时立刻抛出异常来实现迅速失败,可以有效避免不必要的对象构造或资源占用,比如文件或网络连接。同样,打开这些资源所带来的清理操作也可以省却。

延迟捕获

这个是我觉得最重要的一个原则,不要在程序有能力处理异常的时候就去捕获,尤其是许多底层方法和公共方法,因为你并不清楚他的上层调用会对这种情况作出怎样的处理,比如文件路径为空,有些时候上层程序可能知道另一个备用地址,有些时候就需要用户重新输入,是不一样的。

原文参考地址

原文地址:https://www.cnblogs.com/codersun/p/6896048.html