本文链接 https://www.cnblogs.com/hubaijia/p/about-exceptions-1.html

关于exception的基本语法和作用,这里不再赘述,下面记录一下我在项目中关于Exception的一些思考。

一,使用Exception,而不是Error Code

在初始设计项目的时候,有时候我们为了明确错误类型,定义如下结构:

enum ErrCode
{
Ok,
ArgumentErr,
OtherErr1,
OtherErr2
} class SomeClass
{
public ErrCode DealSomething()
{
//.....
return ErrCode.Ok;
}
} class CallerClass
{
public void CallSome(SomeClass some)
{
ErrCode err = some.DealSomething(); if(err == ErrCode.Ok)
{
//....
}
else
{
//.....
}
}
}

使用ErrCode并不是说完全不可以,比如在Web Api调用中返回ErrCode就是不错的选择。

1,吃掉异常

但是在其他情况,使用ErrCode会让使用者痛苦不堪,因为每次调用都要小心谨慎的处理和判断ErrCode,否则就特别容易吃掉异常

对比如下程序片段:

使用ErrCode 使用Exception
class SomeClass
{
public ErrCode DealSomething()
{
//.....
return ErrCode.SomeErr;
}
}
class CallerClass
{
public void CallSome(SomeClass some)
{
some.DealSomething();
giveMoney();
}
}
class SomeClass
{
public void DealSomething()
{
//.....
if(somthingWrong)
{
throw new SomeException(...);
}
}
}
class CallerClass
{
public void CallSome(SomeClass some)
{
some.DealSomething();
giveMoney();
}
}

左侧使用ErrCode因为不小心没有接受处理ErrCode,导致后续代码继续执行;

而使用Exception,即使没有处理,也不会执行后续代码,并且抛出等待上层使用者处理。

此外,Exception还有其他众多好处,比如Stack Trace信息,跨process等等。

所以Exception的本质就是现成的错误处理(error-handling)机制,就不要再去使用自定义的ErrCode了。

二,Exception在项目中的使用

既然明确使用Exception作为我们的错误处理方案。那么具体怎么使用呢?

1,使用细化的Exception

首先要避免以下写法:

class Dal
{
public void Add(Entity entity)
{
//....
if(somthingWrong)
{
throw new Exception("add wrong");
}
//...
}
}

这种写法的问题在于,调用者无法明确Exception的原因,类型,仅仅依靠文档或者message去理解,

当项目越积越多的时候,就是漫天相同的Exception乱飞,日志里充满了各种神奇的message的时候。

我们可以采取多维度细化Exception:类型、错误种类,具体原因。

使用具体类型化的Exception,比如ConnectionException, TimeoutException, 即为每种错误定义一个Exception类型,

这种方法想必大家都很清楚,但是如果一个模块里有十几种错误,而你又有十几个模块呢?

对于这种情况有两种处理方法。

  • 1, 按内部外部划分

    如果一个异常需要模块外部调用者接收和处理的,那么就定义一个具体的Exception类型,比如ConnectionException,TimeoutException;

    如果一个异常是模块内存自处理的,那么只用同一个InnerException(或者其他名字)加上其他信息来区分。(是不是又闻到了ErrCode的味道,没错)。

    这种方法经常在很多开源类库中看到。

  • 2, 一个模块定义一种Exception类型,Exception与ErrCode相结合

    刚说过ErrCode不能用,这里又提到,大家别误会,仔细往下看。

比如如下代码:

class ErrCode
{
public int Code { get; set; }
public string? Name { get; set; }
public string? Message { get; set; }
} class DalException : Exception
{
public ErrCode Code { get; } public DalException(ErrCode errCode, Exception? innerException = null):base(errCode.Message, innerException)
{
Code = errCode;
}
}

ErrCode已经不简简单单是一个enum了,而是有更大的用处。

这样每一个模块都拥有自己Exception类型,每一种错误类型又能得到有效的分类。

这种方法经常在公司项目的业务模块、基础框架模块里用到。

2,使用Exception.Data

上面的代码段,大家是否有点惊讶,哥们儿,你把Message直接干到ErrCode里面固定起来,是不是有点激进啊。

其实这也是在实际开发中,总结出来的经验。尽量不要放任程序员们零散的去写各种Exception的Message。

但是我们可以用Exception.Data来实现更多场景。

比如下面代码段:

static class ExceptionFactory
{
public static DalException OnMigrateError(int oldVersion, int newVersion, string sql, string cause)
{
DalException ex = new DalException(ErrCodes.MigrationErr); ex.Data["OldVersion"] = oldVersion;
ex.Data["NewVersion"] = newVersion;
ex.Data["Cause"] = cause;
}
} class SomeCls
{
void Migration()
{
//.....
throw ExceptionFactory.OnMigrationErr(oldVersion,)
}
}

这样为每种错误固定了具体需要记录的信息,其中cause就是让程序员记录当下的原因。

当然了,你可以重写OnMigrateError来记录另外一种场景。

把异常Exception具体需要的信息,记录在Data这个Dictionary中, 而不是写到Message中的好处,是有助于今后进一步的处理,比如结构化日志。

当然这带来了一个需要关注的问题,就是对Exception.Data的显式处理,因为Exception.ToString()方法,并不打印Data。

在记录日志方面,如果你使用 Serilog, 那么推荐你使用 Serilog.Exceptions, 他对Exception.Data十分友好。

至此,我们在细化Exception上,以此递进的使用了 类型---> ErrorCode ---> 具体cause

3,集中化管理

漫天飞的Exception,不明所以的处理方案,都需要用规则来解决,但是规则需要时刻牢记,所以最靠谱的还是用代码结构来解决。

首先建立ErrCodes静态类,然后建立ExceptionFactory类,这样业务代码里抛出异常都具有throw ExceptionFactory.XXException()这样的形式。

//所有的错误代码
internal static class ErrCodes
{
public static ErrCode MigrationErr { get; } = new ErrCode(1, nameof(MigrationErr), "Error happens in Migration.");
//........ other errors
} //所有的Exception都由此生成
internal static class ExceptionFactory
{
public static DalException OnMigrateError(int oldVersion, int newVersion, string sql, string cause)
{
DalException ex = new DalException(ErrCodes.MigrationErr); ex.Data["OldVersion"] = oldVersion;
ex.Data["NewVersion"] = newVersion;
ex.Data["Cause"] = cause;
}
//.....其他场景
}

这样,code review时,只要看到有程序员在代码中自己 new XXException,那么就督促他在ExceptionFactory里寻找合适的异常场景或者自己添加。

随着项目积累,即使异常、错误种类众多,大家也只需在ErrCodesExcepionFactory两个类中,总结归纳,重构。

4,参考代码

具体的代码,上传在 Github, 欢迎探讨和指正。

三,预告

在下一篇,我将要探讨下关于捕捉异常的话题(这么简单?)。

[.net] 关于Exception的几点思考和在项目中的使用(一)的更多相关文章

  1. [.net] 关于Exception的几点思考和在项目中的使用(三)

    本文链接: https://www.cnblogs.com/hubaijia/p/about-exceptions-3.html 系列文章: 关于Exception的几点思考和在项目中的使用(一) 关 ...

  2. [.net] 关于Exception的几点思考和在项目中的使用(二)

    本文链接: https://www.cnblogs.com/hubaijia/p/about-exceptions-2.html 系列文章: 关于Exception的几点思考和在项目中的使用(一) 关 ...

  3. Android -- 思考 -- 为什么要在项目中使用MVP模式

    1,其实有时候一直在找借口不去思考这个问题,总是以赶项目为由,没有很认真的思考这个问题,为什么我们要在项目中使用MVP模式,自己也用MVP也已经做了两个项目,而且在网上也看了不少的文章,但是感觉在高层 ...

  4. 从有约束条件下的凸优化角度思考神经网络训练过程中的L2正则化

    从有约束条件下的凸优化角度思考神经网络训练过程中的L2正则化 神经网络在训练过程中,为应对过拟合问题,可以采用正则化方法(regularization),一种常用的正则化方法是L2正则化. 神经网络中 ...

  5. VS2013中web项目中自动生成的ASP.NET Identity代码思考

    vs2013没有再分webform.mvc.api项目,使用vs2013创建一个web项目模板选MVC,身份验证选个人用户账户.项目会生成ASP.NET Identity的一些代码.这些代码主要在Ac ...

  6. 由项目中一个hash2int函数引发的思考

    hash2int /** * 计算一个字符串的md5折算成int返回 * @param type $str * @return type */ function hash2int($str) { $m ...

  7. 12月中旬项目中出现的几个bug解决方法的思考

    这周做的项目遇到2个费了很多时间才解决的bug,解决之后,发现根本问题并不是什么很难的技术难点,都是因为自己在写代码的过程中,思维不够清晰.还有一个需要再提高的地方就是解决问题的思维,如何快速定位到问 ...

  8. java Exception 出错的栈信息打印到日志中 打印堆栈信息

    我们在开发程序的过程当中,日志是必不可少的工具,这有助于我们分析问题的原因,和出错的详细信息,而java的异常机制又会方便且迅速的帮我们找到出错行的位置. try { .... } catch (Ex ...

  9. 项目中访问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 ...

随机推荐

  1. how to fetch html content in js

    how to fetch html content in js same origin CORS fetch('https://cdn.xgqfrms.xyz/') .then(function (r ...

  2. 2019 front-end job Interview

    2019 front-end job Interview 2019 前端面试题 掘金 https://juejin.im/tag/面试 https://juejin.im/post/5c875791e ...

  3. 10000星光值兑换一个的VAST将如何搅动NGK算力市场?

    加密数字货币是私人而非政府所发行的数字资产,具有自己的"货币"账户单位,在可以预见的未来三年之内,加密数字货币将覆盖至少全世界五分之一的人口.为此,NGK方面也做出了自己的努力,在 ...

  4. HTML页面顶部出现空白部分(#65279字符?)解决办法

    1.在火狐下面用Firebug,选择body,点编辑html的时候,看到是多出了一个这个代表的意思,还真不知道,搜索后了解到是一种中文的编码规则,   UTF-8不需要BOM来表明字节顺序.   制作 ...

  5. 解决使用Redis时配置 fastjson反序列化报错 com.alibaba.fastjson.JSONException: autoType is not support

    1.问题描述 在使用redis时,配置自定义序列化redisTemplate为FastJsonRedisSerializer .  1 /** 2 * 自定义redis序列化器 3 */ 4 @Sup ...

  6. Pygame基础(1)

    Pygame是Python的一个很常用的游戏框架,今天我来讲一讲Pygame的基础知识 Pygame的官网:https://www.pygame.org/news Pygame的下载 打开cmd输入p ...

  7. Java基本概念:面向对象

    一.简介 面向过程的思维模式是简单的线性思维,思考问题首先陷入第一步做什么.第二步做什么的细节中. 面向对象的思维模式说白了就是分类思维模式.思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独 ...

  8. 第42天学习打卡(Class类 Class类的常用方法 内存分析 类的加载过程 类加载器 反射操作泛型 反射操作注解)

    Class类 对象照镜子后得到的信息:某个类的属性.方法和构造器.某个类到底实现了哪些接口.对于每个类而言,JRE都为其保留一个不变的Class类型的对象.一个Class对象包含了特定某个结构(cla ...

  9. AVR单片机教程——第三期导语

    背景(一) 寒假里做了一个灯带控制器: 理想情况下我应该在一个星期内完成这个项目,但实际上它耗费了我几乎一整个寒假,因为涉及到很多未曾尝试的方案.在这种不是很赶时间的.可以自定目标.自由发挥的项目中, ...

  10. 学习java之基础语法(一)

    学习java之基础语法(一) 基本语法 编写 Java 程序时,应注意以下几点: 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的. 类名:对于所有的类来说 ...