面试官:大学这几年干嘛去了,连异常处理都不知道,我怎么要你?

这是一个真实的案例,之前刚出来找工作的那时候,面试官问了我这个问题,“你平时是怎么处理程序中的异常?”,当时羞涩而紧张,没说出个所以然来。所以后来就记录了这些问题,我有个习惯,从大学那时候就有了,人家问我的问题,我都会记录下来,印象笔记我从大学那时就开始写了。

在实际的工作中,捕获异常,收集分析异常对于解决问题至关重要。掌握正确的方法有利于快速解决问题。

前文传送门 dotNET开发系列

收藏!推荐12个超实用的Visual Studio插件

【原创】StackOverflow 20万关注的问题:如何实现异步Task超时的处理?

使用Vistual Studio N年,推荐2个异常捕获的技巧

面试官:你连RESTful都不知道我怎么敢要你?

01 异常对象

首先了解的就是Exception对象信息。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:是只读属性,GetRuntimeResourceString是获取运行时资源字符串。返回的字符串是产生异常原因的错误消息或者空字符串。

Data:其他异常信息的键/值对的集合

public virtual IDictionary Data {
            get {
                if (_data == null)
                  if(IsImmutableAgileException(this))                            _data = new EmptyReadOnlyDictionaryInternal();
                  else
                   _data = new ListDictionaryInternal();
                  return _data;
            
    }
}

StackTrace:出现异常之前调用的方法名称和签名

public static string StackTrace
{
    [SecuritySafeCritical]
    get
    {
        new EnvironmentPermission(PermissionState.Unrestricted).Demand();
        return GetStackTrace(null, true);
    }
}

Source属性:包含生成异常的应用程序或对象的名称

TargetSite属性:引发当前异常的方法

GetBaseException方法 :返回System.Exception,它是所有异常类的“基”类

02 常见的异常类

异常类型有很多,他们都是继承自SystemException,这些异常类型大概分为以下这几种1.与数组集合有关2.与成员访问有关3.与参数有关4.与算术相关5.IO相关6.当然还有其他的一些异常。

1.与数组集合有关

  • IndexOutOfRangeException类:索引超出范围引发的异常

  • ArrayTypeMismatchException类:数组集合存储数据类型不正确引发的异常

  • RankException类:处理维数错误引发的异常

2.IO有关的异常

与IO相关的异常都继承自IOException类,该类用于处理进行文件输入输出操作时所引发的异常,IOException类的5个直接派生类如下。

  • DirectoryNotFoundException类:没有找到指定的目录而引发的异常。

  • FileNotFoundException类:没有找到文件而引发的异常。

  • EndOfStreamException类:处理已经到达流的末尾而还要继续读数据而引发的异常。

  • FileLoadException类:无法加载文件而引发的异常。

  • PathTooLongException类:文件名太长而引发的异常。

03.成员访问有关的异常

与成员访问相关的异常都继承自MemberAccessException这个类,它继承自SystemException。

  • FileAccessException:访问字段成员失败所引发的异常

  • MethodAccessException:访问方法成员失败引发异常

  • MissingMemberException:成员不存在引发的异常

04.参数相关的异常

与参数有关的异常类ArgumentException都继承自SystemException,处理给方法成员传递参数时发生异常

  • ArgumentOutOfRangeException:当一个参数不在给定范围内引发的异常

  • ArgumentNullException:参数为null(不允许null)的情况下引发的异常

05.与算术相关

  • ArithmeticException异常类用于处理与算术相关的异常,它的相关子类如下

  • DivideByZeroException:整数十进制试图除以0引发的异常(被除数不能为0)

  • NotFiniteNumberException:浮点数运算中出现无穷大或非负值引发的异常

06.其他异常

  • NullReferenceException:当一个对象没有实例化时并引用引发的异常

  • InvalidOperationException:当对方法的调用对象当前状态无效时引发异常

  • InvalidCastException:处理类型转换期间引发的异常

  • OutOfMemoryException:处理内存不足引发的异常

  • StackOverflowException:处理栈溢出引发的错误

异常捕获

c#中提供try 和catch块提供了一种结构化的异常处理方案,所有可能出现的异常都必须得到妥善的处理,try catch本身并不会影响系统的性能,在没有发生异常的时候try catch 是不会影响系统性能的。受影响的时候是发生异常的时候。

关键字 try catch finally。先执行try里面的语句,如果抛出异常就会被catch捕获。无论出不出现异常都会执行finally里面的语句。

思考:下面这代码是否有问题?

class Program
    {
        static void Main(string[] args)
        {
            DivideNumber div = new DivideNumber();
            div.DivideMethod(2, 0);
            Console.ReadKey();
        }
    }
    class DivideNumber
    {
        int result;
        public DivideNumber()
        {
            result = 0;
        }
        public void DivideMethod(int a,int b)
        {
            try
            {
                result = a / b;
            }
            catch (DivideByZeroException e)
            {
                Console.WriteLine("exception,被除数不能为0,e.message:" + e.Message);
            }
            finally {
                Console.WriteLine($"{a}除以{b}的结果是"+result);
            }
        }
    }

异常处理原则和建议

首先,异常处理应该是系统设计规约的一部分出现在系统设计文档中,而不仅仅是一种技术实现。

作为设计文档的一部分,异常处理应该着眼于系统容错性和稳定性(正如楼主提到的那样)。然后在根据这个规约,再来具体讨论和选择异常处理中使用的各种技术细则。

比如,在设计服务时,必须在服务的调用接口处有异常处理,否则客户端传过来的任何有害数据都可能让服务器挂掉。

比如,对异常的处理在系统的设计中,必须有明确说明,不能随便在哪个模块中处理异常。

以上是我的个人经验,还望走过的朋友多多交流。

要捕获具体的异常

在捕获异常的时候,我们经常习惯性写catch(Exception ex) ,这个并非具体的异常,最好是能具体到ArgumentException、FormatException等异常类,不要抛出”new Exception()” catch中啥也不干,异常要向顶层抛出 这种情况在自己写demo的时候可能比较常见,在编写catch(Exception ex)这块代码下啥也不干,不要这样做。切记出现的异常要想顶层抛出

合理使用finally块

finally关键字是不管抛出什么类型异常都会被执行,大多数的时候能在finally块下执行的代码,也能写在catch里面。那么finally关键字到底在什么情况下使用比较合适呢,比如清理资源,关闭流,回复状态等。

抛出的异常要记录下来

当然程序中出现的异常并不是所有都要记录下来,有些异常还是记录下来便于分析具体的问题。一些记录日志库 log4net ,EIF…… 不要只记录Exception.Message的值,还需要记录Exception.ToString()

刚刚前面的例子,我打印的e.Message ,仅仅只是输出“尝试除以0”,提示的错误信息不具体,并不推荐这样做。Tostring方法中包含了stacktrace、内部异常信息、Message……通常这些信息比仅一个Message更重要

不要将“抛出异常”作为函数执行结果的一种

“抛出异常”应该向顶层抛出,但是不能作为方法执行结果的一种,方法的结果不能是异常类。

每个线程要包含一个try/catch块

创建子线程去执行任务时,主线程不会知道子线程的异常情况,所以每个线程都需要一个try、catch.

推荐阅读

原创StackOverflow 20万关注的问题:如何实现异步Task超时的处理?

悲剧34岁中国工程师在美遭抢劫,追车被拖数十米身亡!当天还是他生日

面试分享昨天面试5道有意思的题目

汇总2019我的公众号回顾

原创程序员:这10种糟糕的程序命名,你遇到过几个?

原创使用Vistual Studio N年,推荐2个异常捕获的技巧

面试面试官:你连RESTful都不知道我怎么敢要你?

转载程序员:改完这9段屎一样的代码,还挺香


扫码关注我们

dotNet全栈开发

入群方式:公众号内回复"加群"

关键字回复:"资源"

领取c#|前端 入门进阶教程资源

发布了124 篇原创文章 · 获赞 4563 · 访问量 153万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术工厂 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览