[.net] 关于Exception的几点思考和在项目中的使用(二)
本文链接: https://www.cnblogs.com/hubaijia/p/about-exceptions-2.html
系列文章:
本文目录
与Exception相关的文件结构
在上一篇我们提到了 集中化管理 Exceptions,这样所有的Exception的产生都在同一文件中,便于我们后期的Review、重构,不会看到漫天飞舞的throw new Exceptions(...)
,也尽量避免了同一种类的Exception拥有着不同的Message,记录着不同的参数信息。
此外在上一篇中我们还提出了细化Exception的方式,即按照 类型、ErrorCode、Cause 以此细化。
举例来说,项目中会形成如下的文件结构:(实例代码请访问 Github)
绿框里的就是Exception相关的代码文件。其中XXX_ErrorCodes
, XXX_ExceptionFactory
等类型应该标记为internal
,即只是类库内部可见。
在上述文件结构中,我简要的模拟了一个项目的分层结构,不一定都是这样,但大致如下:
- Common 是项目公共类库,放着各种辅助代码、框架代码等等,代表着你项目的基础部分;
- Dal 放着连接各种Store(MySql、Redis等等)的代码,即放DataAccessObject的;
- Repo 屏蔽各种细节,直接为各种Service提供服务;
- Services 表示你的各项业务模块,当然你的项目结构中有不同的叫法和含义,大家各自知道就行;
- Application 具体的应用,提供Endpoint,开放Service的各项功能,面向调用者。
为什么要详细描述一个项目分层结构,因为一会儿我们需要解决一个问题,就是Exception的可见性。
先卖个关子,一会儿再说,在此之前,先解决一个小问题。
ExceptionFactory 和 Ensure
将Exception集中化管理,除了我们用的ExceptionFactory,还有一种写法我们经常见到,那就是Ensure,或者EnsureXXX。
参阅.net core的源代码,我们会看到非常多的Ensure类,源代码连接 https://source.dot.net/#q=Ensure。
我们经常把 复杂 或者 重复 出现的判断写到Ensure中,Ensure的中文意思“确保”正是这个含义。
示例代码如下:
// Ensure 写法
internal static class Ensure
{
public static void NickNameNotExisted(string newNickName)
{
bool alreadyExisted = true;
// Some Check
if(alreadyExisted)
{
throw IdentityExceptionFactory.NickNameExisted();
}
}
public static T NotNull<T>([NotNull]T? obj, string paramName) where T : class
{
if (obj == null)
{
throw new ArgumentNullException(paramName);
}
return obj;
}
}
我们观察到,Ensure类中的Exception也是由ExceptionFactory
生成的(当然那些基础Exception类型,比如ArgumentNullException
就直接new 了)。
即Ensure
类是对判断(if语句等)的抽象,而ExceptionFactory只是Exception的生成器。
当然,当我们不直接在现场抛出异常,而是调用Ensure类方法抛出异常,会稍微有一些不同:
- Stack Trace不同,第一行永远是Ensure的方法
- 没有什么大不了,因为Stack Trace会详细记录后续的调用;
- 代码逻辑上不如直接写 throw 那么直白
- 明确使用Ensure这一称呼,写到代码规则里,团队内统一,一看到Ensure就知道可能要抛出异常。
- 注意CodeAnalysis 和 NRT(null reference types)
- 如果项目开启上面两项功能,需要添加相应的Attribute。比如,上面代码的
NotNull
方法,否则在Ensure后,CodeAnalysis还会提醒你CS8602等错误.
- 如果项目开启上面两项功能,需要添加相应的Attribute。比如,上面代码的
Exception的可见性
举例来说,就是 DalException
要不要对Service可见,还是只对Repo可见?
其实可见或者不可见都没有明显的错误,但总的来说,
- 隐藏并包装下层的Exception,对调用者更友好些。
- 比如在WebApplication项目中,我们就可以只面对来自Service的Exception,不用去知道Dal或者Repo的细节。
- 而来自IdentityService的异常,只有IdentityException一种(不算ArgumentNull那些基本Exception)
- 隐藏并包装下层的Exception,可以在每一层加入更多有用的信息
- 如果是调用第三方类库,尽量隐藏和包装成自己的异常类型。比如你在Dal中调用MySqlConnector类库,那么将
MySqlException
放到DalException的innerException中去。让程序的其他部分不需要面对一个可能更换掉的外来者。
结语
以上写的都比较具体,所以如果与大家的实际使用不同,请多多交流,共同进步,我也会补充到文章里来。
下一篇,我会讲讲关于 捕捉Exception的几点实际经验,比如全局异常处理以及异步编程中的异常。
谢谢阅读。
[.net] 关于Exception的几点思考和在项目中的使用(二)的更多相关文章
- [.net] 关于Exception的几点思考和在项目中的使用(三)
本文链接: https://www.cnblogs.com/hubaijia/p/about-exceptions-3.html 系列文章: 关于Exception的几点思考和在项目中的使用(一) 关 ...
- [.net] 关于Exception的几点思考和在项目中的使用(一)
本文链接 https://www.cnblogs.com/hubaijia/p/about-exceptions-1.html 关于exception的基本语法和作用,这里不再赘述,下面记录一下我在项 ...
- Android -- 思考 -- 为什么要在项目中使用MVP模式
1,其实有时候一直在找借口不去思考这个问题,总是以赶项目为由,没有很认真的思考这个问题,为什么我们要在项目中使用MVP模式,自己也用MVP也已经做了两个项目,而且在网上也看了不少的文章,但是感觉在高层 ...
- 从有约束条件下的凸优化角度思考神经网络训练过程中的L2正则化
从有约束条件下的凸优化角度思考神经网络训练过程中的L2正则化 神经网络在训练过程中,为应对过拟合问题,可以采用正则化方法(regularization),一种常用的正则化方法是L2正则化. 神经网络中 ...
- VS2013中web项目中自动生成的ASP.NET Identity代码思考
vs2013没有再分webform.mvc.api项目,使用vs2013创建一个web项目模板选MVC,身份验证选个人用户账户.项目会生成ASP.NET Identity的一些代码.这些代码主要在Ac ...
- 由项目中一个hash2int函数引发的思考
hash2int /** * 计算一个字符串的md5折算成int返回 * @param type $str * @return type */ function hash2int($str) { $m ...
- 12月中旬项目中出现的几个bug解决方法的思考
这周做的项目遇到2个费了很多时间才解决的bug,解决之后,发现根本问题并不是什么很难的技术难点,都是因为自己在写代码的过程中,思维不够清晰.还有一个需要再提高的地方就是解决问题的思维,如何快速定位到问 ...
- java Exception 出错的栈信息打印到日志中 打印堆栈信息
我们在开发程序的过程当中,日志是必不可少的工具,这有助于我们分析问题的原因,和出错的详细信息,而java的异常机制又会方便且迅速的帮我们找到出错行的位置. try { .... } catch (Ex ...
- 项目中访问controller报错:HTTP Status 500 - Servlet.init() for servlet spring threw exception
直接访问controller路径http://localhost:8080/index报错: HTTP Status 500 - Servlet.init() for servlet spring t ...
随机推荐
- eui & search select
eui & search select https://element.eleme.io/#/zh-CN/component/select demo <template> < ...
- 比特币市场活跃,VAST发行在即!
截至1月25日13:30,BTC合约多空持仓人数比为1.44,市场做多人数占据优势:季度合约基差保持在1255美元上方,永续合约资金费率为正,交割及永续合约持仓总量为19.5亿美元,总体上多军占优:B ...
- opencv打不开摄像头
问题描述: capFace = cv2.VideoCapture(0) 报错: VIDEOIO ERROR: V4L2: Unable to capture video memory. VIDEOIO ...
- 一次"内存泄漏"引发的血案
本文转载自一次"内存泄漏"引发的血案 导语 2017年末,手Q春节红包项目期间,为保障活动期间服务正常稳定,我对性能不佳的Ark Server进行了改造和重写.重编发布一段时间后, ...
- 鸿蒙Java开发模式11:鸿蒙图片裁剪功能的实现
鸿蒙入门指南,小白速来!从萌新到高手,怎样快速掌握鸿蒙开发?[课程入口] 目录: 1. 鸿蒙版图片裁剪功能效果展示 2.Java代码实现 3.裁剪工具类实现 4.<鸿蒙Java开发模式>系 ...
- Laravel Queues 队列应用实战
队列,顾名思义,排着队等着做事情.在生活场景中,凡是排队的人,都是带有目的性的.要完成某件事情,才去排队的,要不没有谁会闲到排队玩儿.而在软件应用层面,队列是什么,队列有什么优点,我们什么时候需要用队 ...
- 能取值亦能赋值的Python切片
切片,就像面包,给几刀,切成一片一片,可以做成吐司,也可以做成三明治,口味更佳: 列表(list).元组(tuple).字符串(str)都能进行切片,得到子片段,实际上切片操作比想象的要强大很多,能取 ...
- docker轻量级监控-sysdig
sysdig Sysdig = system(系统)+dig(挖掘).Sysdig 是一个开源系统发掘工具,用于系统级别的勘察和排障,可以把它看作一系列Linux系统工具的组合,主要包括: strac ...
- C#.NET操作数据库通用类
下面给出了一个C#操作MS SQL Server 数据库的通用类,通过该类可以对数据库进行任何操作,包括执行SQL语句.执行存储过程.以下是其详细实现过程,希望大家共同修改优化之.稍后将介绍如何使用它 ...
- C#深度复制和浅度复制
C#深度复制和浅度复制 复制一个值变量很简单,新建一个变量然后将原来的变量赋值过去就行,但是复制一个引用变量这种方法是不行的,如果不明白为什么可以先看看这篇解释 引用类型变量和值类型变量在赋值时的不同 ...