C# 基础知识系列- 15 异常处理篇
0. 前言
为什么我们需要异常处理?什么是异常?
在汉语中,异常指非正常的;不同于平常的。翻译到程序中,就是指会导致程序无法按照既定逻辑运行的意外,或者说是错误。可能会有小伙伴好奇了,我们的程序不是正常的吗,为什么还会出错呢?
我来举几个例子:
- 程序需要访问一个文件,但这个文件不存在,当程序尝试打开一个读该文件的流时就会出错
- 成绩管理系统中,成绩需要一个浮点型的数字,但是输入的人错误的输入了其他符号或者用中文输入了成绩
- 程序需要通过网络与其他服务器进行交互,但是程序所在计算机没有网了
- 程序在计算一个数除以另一个数的时候,除数错误的设置为0了
等等,以上都是出现异常的情景。
那么为什么需要异常处理机制呢?这是因为我们需要我们的程序不能是一个精美的易碎品,所以必须有一定程度的容错性,或者叫强壮性。这时候就要求程序员在开发过程中,对一些可能出现的场景进行预估,然后预先处理这些错误。而异常处理机制使得程序员更加简单方便的处理这些错误。
1. 异常类
C#中,所有异常都继承自System.Exception类,Exception类定义了C#异常应该具有的信息和方法。值得注意的属性有:
public virtual string Message { get; }// 错误的信息,文字描述
public virtual string StackTrace { get; }// 发生异常的调用堆栈信息
public System.Reflection.MethodBase TargetSite { get; }//引发这个错误的方法
public Exception InnerException { get; }// 子异常
解释一下,调用堆栈指的是调用方法的列表。因为在实际开发中,方法的调用大多是一层套一层的形式调用的,而调用堆栈指的就是引发异常的方法到最外层的调用层次。(描述不太准确,大家意会即可)
而子异常或者内部异常,是因为在处理异常的时候,经常会对底层异常做处理然后将底层的异常进行封装和包装然后传递给上一级,使得越接近客服异常的信息越简单明了。
1.1 如何处理异常
之前说了一堆,但是如何处理异常呢?
在C#中,处理异常是一套通用的流程,涉及到三个关键字:try/catch/finally。先看一下写法:
try
{
//可能会抛出异常
}
catch (System.Exception e)
{
// 处理异常
}
简单介绍一下,try块里写的是可能会出现异常的代码。这是因为C#的机制,并不强制性声明方法会抛出异常。也就是说,C#的异常可以在合适的地方处理也可以不处理。
catch块用来声明捕获的异常,catch有三种写法:
try
{
//
}
catch (System.Exception e)// 1
{
//
}
catch(System.Exception)//2
{
//
}
catch//3
{
}
- 声明捕获一个异常,并获取这个异常实例 e
- 声明捕获一个异常,但不使用这个实例
- 声明捕获所有异常,不指定捕获的异常,也不获取异常实例
catch多次使用,意思是多次捕获不同的异常。如示例中的写法,但是示例中的写法存在一定问题。这是因为C#的异常捕获机制引起的,C#的异常捕获要求先捕获特殊的异常,再捕获一般的异常。换句话就是,在异常类继承树中,越是靠近Exception的异常类越是最后catch,在所有可能的异常处理中,Exception最后处理。所以catch可以是不在一个继承树上的异常类并列处理,也可以先子类再父类这种方式处理,但不论如何都不能对同一个异常多次catch。而且,一旦上一个catch了Exception,则之后的catch全都不会起作用。
finally块在异常处理中并不一定需要出现,但是这个块在异常处理中有着特殊的意义。finally块表示最后执行的块,用finally包裹的代码必然会执行。通常finally用来处理一些托管资源的释放和流的关闭等类型。
1.2 如何抛出一个异常
在上一节我们简单介绍了一下如何处理异常,这一节我们演示一下如何抛出一个异常。
使用throw就可以了,简单演示一下如何抛出异常:
static void Main(string[] args)
{
throw new Exception();
}
这是最简单的写法,在方法中引发一个异常然后抛出。
这时候回过头来看一下Exception有哪些构造方法:
public Exception ();
public Exception (string message);
public Exception (string message, Exception innerException);
所以我们在抛出异常的时候,可以指定异常的信息(message),其中堆栈信息和调用方法等内容由C#底层代码自动填写。
1.3 如何创建一个自定义异常
在简单演示了如何处理异常和如何抛出异常之后,我们来看看如何自定义一个异常类。根据类继承原则和异常处理原则,我们可以使用以下方式来自定义一个类:
public class CustomException : Exception
{
}
这样我们就能获取一个异常类,我们可以根据自己的需要定制这个异常类,然后在使用的时候使用throw抛出。
2. 演示异常处理
class Program
{
static void Main(string[] args)
{
try
{
ThrowAnExcetption();
}
catch(CustomException e)
{
Console.WriteLine(e.StackTrace);
}
finally
{
Console.WriteLine("执行了finally方法");
}
}
public static void ThrowAnExcetption()
{
throw new CustomException();
}
}
public class CustomException : Exception
{
}
以上示例简单演示了如何抛出异常,处理异常。
3. 总结
异常处理很简单,但是也很难。简单是指使用起来很简单,很难说的是在项目中如何合理优雅的处理异常和抛出异常。
这里是我自己总结的一个异常处理的哲学:
- 不是必须的场景,不要抛出异常
- 底层异常不要直接抛给上层方法
- 在程序编写的期间,预估一些场景,并对这些场景做数据校验和提示,而不是使用异常
- 在捕获异常时,最好编写相应的处理逻辑,而不是为了程序不报错直接写一个空的catch块
- 不要把异常当做额外的返回值处理
当然,最重要的一点就是结合实际业务需要进行异常处理。
C#的异常对于程序员来说,不是强制的,但是程序员必须在开发过程中对异常足够的重视才行。
更多内容烦请关注我的博客《高先生小屋》
C# 基础知识系列- 15 异常处理篇的更多相关文章
- C# 基础知识系列- 14 IO篇 文件的操作 (3)
本篇继续前两篇内容,跟大家介绍一下Path类以及FileSystemInfo这个类的主要方法和属性. 上文提到,在<C# 基础知识系列-IO篇>之文件相关的内容完结之后,会带领大家开发一个 ...
- C# 基础知识系列- 14 IO篇 流的使用
0. 前言 继续之前的C# IO流,在前几篇小短片中我们大概看了下C# 的基础IO也对文件.目录和路径的操作有了一定的了解.这一篇开始,给大家演示一下流的各种操作.以文件流为例,一起来看看如何操作吧. ...
- C# 基础知识系列- 17 实战篇 编写一个小工具(1)
0. 前言 这是对C# 基础系列的一个总结,现在我们利用之前学到的知识做一个小小的工具来给我们使用. 如果有看过IO篇的小伙伴,应该有印象.当时我提过一个场景描述,我们在平时使用系统的时候,经常会为了 ...
- C# 基础知识系列- 14 IO篇 文件的操作
0. 前言 本章节是IO篇的第二集,我们在上一篇中介绍了C#中IO的基本概念和一些基本方法,接下来我们介绍一下操作文件的方法.在编程的世界中,操作文件是一个很重要的技能. 1. 文件.目录和路径 在开 ...
- C# 基础知识系列- 14 IO篇之入门IO
0. 前言 在之前的章节中,大致介绍了C#中的一些基本概念.这篇我们将介绍一下C#的I/O操作,这将也是一个小连续剧.这是第一集,我们先来简单了解一下C#中的I/O框架. 1. 什么是I/O I/O ...
- C# 基础知识系列- 16 开发工具篇
0. 前言 这是C# 基础知识系列的最后一个内容讲解篇,下一篇是基础知识-实战篇.这一篇主要讲解一下C#程序的结构和主要编程工具. 1. 工具 工欲善其事必先利其器,在实际动手之前我们先来看看想要编写 ...
- C# 基础知识系列- 3 集合数组
简单的介绍一下集合,通俗来讲就是用来保管多个数据的方案.比如说我们是一个公司的仓库管理,公司有一堆货物需要管理,有同类的,有不同类的,总而言之就是很多.很乱.我们对照集合的概念对仓库进行管理的话,那么 ...
- 基础知识系列☞Abstract和Virtual→及相关知识
转载地址→http://www.cnblogs.com/blsong/archive/2010/08/12/1798064.html 在C#的学习中,容易混淆virtual方法和abstract方法的 ...
- C# 基础知识系列- 9 字符串的更多用法(一)
0. 前言 在前面的文章里简单介绍了一下字符串的相关内容,并没有涉及到更多的相关内容,这一篇将尝试讲解一下在实际开发工作中会遇到的字符串的很多操作. 1. 创建一个字符串 这部分介绍一下如何创建一个字 ...
随机推荐
- Python爬虫系列(一):从零开始,安装环境
在上一个系列,我们学会使用rabbitmq.本来接着是把公司的celery分享出来,但是定睛一看,celery4.0已经不再支持Windows.公司也逐步放弃了服役多年的celery项目.恰好,公司找 ...
- 区间dp入门+例题
区间dp作为线性dp的一种,顾名思义是以区间作为阶段进行dp的,使用它的左右端点描述每个维度,决策往往是从小状态向大状态转移中推得的.它跟st表等树状结构有着相似的原理---向下划分,向上递推. dp ...
- 手动搭建I/O网络通信框架4:AIO编程模型,聊天室终极改造
第一章:手动搭建I/O网络通信框架1:Socket和ServerSocket入门实战,实现单聊 第二章:手动搭建I/O网络通信框架2:BIO编程模型实现群聊 第三章:手动搭建I/O网络通信框架3:NI ...
- AJ学IOS(05)UI之360等下载管理器九宫格UI
AJ分享,必须精品 先看效果 主要是完成了九宫格UI的搭建 代码 - (void)viewDidLoad { [super viewDidLoad]; //九宫格中每个格子的宽 #define kAp ...
- delphi使用ADO在sql数据库存取图片的方法
我一直不认为能把代码写的和天书一样的程序员是好的程序员,那不过是因为我真的对delphi也就是略懂皮毛,太深了看不懂.网上查询数据库存取图片的方式,看的是一头雾水,有人提出保存路径使用时再调用,方法很 ...
- C++11<functional>深度剖析
自C++11以来,C++标准每3年修订一次.C++14/17都可以说是更完整的C++11:即将到来的C++20也已经特性完整了. C++11已经有好几年了,它的年龄比我接触C++的时间要长10倍不止吧 ...
- poi导出word文档,doc和docx
maven <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><dependency> <gro ...
- Spring5:控制反转
二.Spring IOC控制反转 1:IOC推导 >传统业务调用编程 定义一个userDao接口:UserDao package com.spring; public interface Use ...
- leetcode-0543 二叉树的直径
题目地址https://leetcode-cn.com/problems/diameter-of-binary-tree/ 递归+BFS(暴力解法) 我们可以考虑在每个节点时,都去计算该节点左子树和右 ...
- python 异步Web框架sanic
我们继续学习Python异步编程,这里将介绍异步Web框架sanic,为什么不是tornado?从框架的易用性来说,Flask要远远比tornado简单,可惜flask不支持异步,而sanic就是类似 ...