重学c#系列——异常续[异常注意事项](七)
前言
对上节异常的补充,也可以说是异常使用的注意事项。
正文
减少try catch的使用
前面提及到,如果一个方法没有实现该方法的效果,那么就应该抛出异常。
如果有约定那么可以按照约定,如果约定有歧义,那么还是要抛出异常。
我们知道使用try catch 其实是要消耗性能的,那么是否能避免使用try catch呢?或者减少使用try catch呢?
我们使用api的时候,知道http制定了一套错误码,那么我们是否能使用错误码返回来做一个约定的呢?
答案是否定的,且不论我们的业务的不同,错误码制定的困难,单是方法里面使用错误码偶和性就非常大。
既然无法通过约定一套错误码来解决,那么是否可以先验证该方法能否执行成功呢?
public class ArrayWorker
{
int[] arrays = { 10, 9, 8, 7, 6 };
public bool tryWorker(int index)
{
if (arrays.Length >index&&index>-1)
{
return true;
}
return false;
}
public int DoWorker(int index)
{
return arrays[index];
}
}
调用:
static void Main(string[] args)
{
ArrayWorker arrayWorker = new ArrayWorker();
if (arrayWorker.tryWorker(-1))
{
arrayWorker.DoWorker(-1);
}
else
{
//错误处理
}
}
调用前可以先检测是否可以执行成功,如果执行成功再去执行,如果不行进行另外的处理。
当然这种存在局限性,比如说检查条件复杂等。
异常块using 和 try finally
我们知道如果非托管资源,且实现了正常的IDisposable,那么我们有两个时候可以去释放资源,一个是我们主动调用dispose,还有一个我们在finalizer中释放。
那么哪个好呢?当然是dispose中了,原因很简单,如果在finalizer中释放,意味着我们的非托管资源在内存中的事件下降,还有一个我在托管中提及到的所以终结器是单线程,我们是不希望放在这里面去非托管资源的,因为堵塞。
我通常使用using来避免自己忘记释放资源,有时候也使用try finally,这样写是否代码风格不统一呢?还是说他们之间有区别呢?
public static void excecuteCommond(string connectString, string commonString)
{
SqlConnection sqlConnection = null;
try
{
sqlConnection = new SqlConnection(connectString);
SqlCommand sqlCommand = null;
try
{
sqlCommand = new SqlCommand(commonString, sqlConnection);
}
finally
{
sqlCommand?.Dispose();
}
}
finally
{
sqlConnection?.Dispose();
}
}
public static void excecuteCommond1(string connectString, string commonString)
{
using (SqlConnection sqlConnection = new SqlConnection(connectString))
{
using (SqlCommand sqlCommand = new SqlCommand(commonString,sqlConnection))
{
}
}
}
上面两个方法在IL上是相等的。实际上using是try finally的语法糖。但是呢,我们写嵌套using,实际上是写嵌套try finally。
有些人不喜欢嵌套try finally,所以会这样写:
public static void excecuteCommond(string connectString, string commonString)
{
SqlConnection sqlConnection = null;
SqlCommand sqlCommand = null;
try
{
sqlConnection = new SqlConnection(connectString);
sqlCommand = new SqlCommand(commonString, sqlConnection);
}
finally
{
sqlCommand?.Dispose();
sqlConnection?.Dispose();
}
}
然后在非托管异常处理中,发现sqlConnection不仅提供Dispose方法还是存在close。
这个close 和dispose什么区别呢?在该系列非托管中,Dispose不仅释放自己的资源,还干了一件事就是GC.SuppressFinalize(),也就是说去抑制终结器。
那么close没有去抑制终结器,所以区别还是挺大的。
而且在设计上,close 之后,还是可以open connect的,而dispose后不能open connect。close 是去释放现在占有的资源(相当于没收财产,加个死缓),dispose是标记这个对象进入死亡阶段(死刑,一般来说救不回来了)。
异常保证
常说,通过try catch来捕获异常,万一真的出现异常怎么破?是不是应该做点啥?
处理异常实际上,需要三种保障。
1.基本保障。
2.强保证
3.no-throw 保障
分别介绍这三种不同的意思:
1.基本保证的要求是:确保异常离开产生该异常的函数后,程序中的资源不会泄露,而且所以对象处于有效状态。
2.强保证是在基本保证的前提下,他要求整个程序的状态不能因为某操作产生异常而变化。
3.no-throw 是程序不会发生异常。
那么这三种类型中,强保证是首推的。
单纯基本保证感觉又不够,no-throw 太严格,强保证是一种折中方案。
强保证规定:如果操作出现异常,那么应用程序状态必须和执行前操作是一样的,这样说比较诡异,简单点说是这样子的。
执行方法的操作要么是完全成功,要不是完全失败。如果失败,那么程序的状态就和执行操作之前一模一样,而不会出现部分成功的情况。
这不难想象到,如果我们调用一个方法出现了异常,如果方法中某一步出现异常,而数据发送了改变,但是剩余操作无法执行,这时候我们的程序数据将不可控。
那么这个时候我们如何去实现这个强类型保证呢?通过防御性的拷贝来实现,具体步骤如下:
1.先把要修改的数据拷贝一份。
2.在拷贝的数据上面修改,修改过程中,执行那些可能抛出异常的操作。
3.完全操作成功用拷贝文件去覆盖原数据。
这么一看,这不是cpu和内存损耗吗?
是的,但是这增加了程序的健壮性吗?随着时代的发展,分布式集群的发展,性能似乎可以用硬件来堆,程序的稳健性应该放在第一位。
我们是不是保证了强类型异常,那么是不是no-throw 永远用不到呢?其实提出3个标准的时候,是按照程序的严格顺序来的。强类型异常在异常下,无法继续执行了,但是数据不能部分修改。
那么no-throw用在什么地方呢?
下面几个地方一定不能出现异常。
1.finalizer 与 dispose中不能出现异常。
2.异常筛选器中不能出现异常。
3.委托目标中不能出现异常。
第一个在finalizer 中出现异常,那么线程会中断,可以想象一下终结器中断是啥子问题。
dispose中释放非托管中出现问题,资源问题即大问题,因为不好恢复。
异常筛选器中一旦出现异常,那么原先抛出的异常将会被新的异常覆盖,捕获不到原先的异常,就会出现艰难的错误处理问题。
委托目标这个很好理解了,多播委托中,一旦出现问题,那么后续的方法不会执行。
这个时候在可能出现异常的地方使用try catch,捕获异常,从而不会中断程序。
结
这一章是对上一节的补充,还有很多细节是无法写完的,就到此吧,后续是一个自定义异常的例子。
以上只是个人整理,如有问题,请指出。最后卑微的说一句:跪求大佬指点 。

重学c#系列——异常续[异常注意事项](七)的更多相关文章
- 重学c#系列——字典(十一)
前言 重学c#系列继续更新,简单看一下字典的源码. 看源码主要是解释一下江湖中的两个传言: 字典foreach 顺序是字典添加的顺序 字典删除元素后,字典顺序将会改变 正文 那么就从实例化开始看起,这 ...
- 重学c#系列——异常(六)
前言 用户觉得异常是不好的,认为出现异常是写的人的问题. 这是不全面,错误的出现并不总是编写程序的人的原因,有时会因为应用程序的最终用户引发的动作或运行代码的环境而发生错误,比如你用android4去 ...
- 重学c#系列——对c#粗浅的认识(一)
前言 什么是c#呢? 首先你是如何读c#的呢?c sharp?或者c 井? 官方读法是:see sharp. 有没有发现开发多年,然后感觉名字不对. tip:为个人重新整理,如学习还是看官网,c# 文 ...
- 重学c#系列——c# 托管和非托管资源与代码相关(四)
前言 这是续第三节. 概况垃圾回收与我们写代码的关系: 强引用和弱引用 针对共享 Web 承载优化 垃圾回收和性能 应用程序域资源监视 正文 强引用和弱引用 垃圾回收器不能回收仍在引用的对象的内存-- ...
- 重学c#系列——list(十二)
前言 简单介绍一下list. 正文 这里以list为介绍. private static readonly T[] s_emptyArray = new T[0]; public List() { t ...
- 重学c#系列——盛派自定义异常源码分析(八)
前言 接着异常七后,因为以前看过盛派这块代码,正好重新整理一下. 正文 BaseException 首先看下BaseException 类: 继承:public class BaseException ...
- 重学Golang系列(一): 深入理解 interface和reflect
前言 interface(即接口),是Go语言中一个重要的概念和知识点,而功能强大的reflect正是基于interface.本文即是对Go语言中的interface和reflect基础概念和用法的一 ...
- 重学c#系列——c# 托管和非托管资源(三)
前言 c# 托管和非托管比较重要,因为这涉及到资源的释放. 现在只要在计算机上运行的,无论玩出什么花来,整个什么概念,逃不过输入数据修改数据输出数据(计算机本质),这里面有个数据的输入,那么我们的内存 ...
- 重学c#系列——datetime 和 datetimeoffset[二十一]
前言 简单介绍一下datetime和 datetimeoffset. 正文 了解一个国家的文化,就要了解一个国家的历史. 要了解datetimeoffset,那么很有必要了解一下datetime. 表 ...
随机推荐
- Python 列表生成式 生成器
[x for x in os.listdir("F:\XXX")] 生成器(x * x for x in range(10)) 如果列表元素按照某种算法推算出来,那我们就可以在循环 ...
- JVM 专题六:运行时数据区(一)概述
1. 运行时数据区架构图 2. 内存 内存是非常重要的系统资源,是硬盘和cpu的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了JAVA在运行过程中内存申请.分配.管理的策略, ...
- python 爬虫,网页转PDF:OSError: No wkhtmltopdf executable found
解决办法: 代码中设置参数: path_wk = r‘D:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe‘ #wkhtmltopdf安装位置 config ...
- java 基本语法(十七)Lambda (四)构造器引用与数组引用
1.构造器引用格式:类名::new 2.构造器引用使用要求:和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致.抽象方法的返回值类型即为构造器所属的类的类型 3.构造器引用举例: / ...
- 数据可视化之powerBI基础(七)一文带你熟悉PowerBI建模视图中的功能
https://zhuanlan.zhihu.com/p/67316729 PowerBI 3月的更新,正式发布了建模视图,而之前只是预览功能.新的建模视图到底有什么用,下面带你认识一下它的主要功能. ...
- 临时解决GitHub的raw.githubusercontent.com无法连接问题
http://qjzd.net:3000/topic/5e48cc33dcf06d6a181ffb81 查询真实IP 通过IPAddress.com首页,输入raw.githubusercontent ...
- 利用服务器+jsDelivr+GitHub搭建咱的免费图床
前言 有人就问了你这个标题写错了?不应该是PicGo+jsDelivr+GitHub搭建咱的免费图床吗? 2333,是一个上传程序啊,不系写错了. 程序介绍 目前程序已实现两种模式上传到GitHub或 ...
- json:server 本地搭建
做个记录, 第一步,我们新建一个文件夹. 第二步,打开文件夹,执行git,没有git可以下载一个.或者用命令行工具进入到这个文件夹! 第三步,初始化json 在git里执行npm init --ye ...
- 基于Scrapy的B站爬虫
基于Scrapy的B站爬虫 最近又被叫去做爬虫了,不得不拾起两年前搞的东西. 说起来那时也是突发奇想,想到做一个B站的爬虫,然后用的都是最基本的Python的各种库. 不过确实,实现起来还是有点麻烦的 ...
- ElasticSearch(四)查询、分词器
正向索引 正排表是以文档的ID为关键字,表中记录文档中每个字的位置信息,查找时扫描表中每个文档中字的信息直到找出所有包含查询关键字的文档. 这种组织方法在建立索引的时候结构比较简单,建立比较方便且易于 ...