C# 9.0 新特性之参数非空检查简化
阅读本文大概需要 1.5 分钟。
参数非空检查是缩写类库很常见的操作,在一个方法中要求参数不能为空,否则抛出相应的异常。比如:
public static string HashPassword(string password)
{
    if(password is null)
    {
        throw new ArgumentNullException(nameof(password));
    }
    ...
}
当异常发生时,调用者很容易知道是什么问题。如果不加这个检查,可能就会由系统抛出未将对象引用为实例之类的错误,这不利于调用者诊断错误。
由于这个场景太常见了,于是我经常在我的项目中通过一个辅助类来做此类检查。这个类用来检查方法参数,所以命名为 Guard,主要代码如下:
public static class Guard
{
    public static void NotNull(object param, string paramName)
    {
        if (param is null)
        {
            throw new ArgumentNullException(paramName);
        }
    }
    public static void NotNullOrEmpty(string param, string paramName)
    {
        NotNull(param, paramName);
        if (param == string.Empty)
        {
            throw new ArgumentException($"The string can not be empty.", paramName);
        }
    }
    public static void NotNullOrEmpty<T>(IEnumerable<T> param, string paramName)
    {
        NotNull(param, paramName);
        if (param.Count() == 0)
        {
            throw new ArgumentException("The collection can not be empty.", paramName);
        }
    }
    ...
}
这个类包含了三个常见的非空检查,包括 null、空字符串、空集合的检查。使用示例:
public static string HashPassword(string password)
{
    Guard.NotNull(password, nameof(password));
    ...
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector)
{
    Guard.NotNullOrEmpty(source, nameof(source));
    ...
}
介于这种非空检查极其常见,C# 9.0 对此做了简化,增加了操作符‘!’,放在参数名后面,表示此参数不接受 null 值。使用方式如下:
public static string HashPassword(string password!)
{
    ...
}
简化了很多有木有。这个提案已经纳入 C# 9.0 的特性中,但目前(2020-06-13)还没有完成开发。
这个特性只支持非 null 检查,其它参数检查场景还是不够用的,我还是会通过辅助类来进行像空字符串、空集合的检查。
这个特性在写公共类库的时候很有用,但我想大多数人在写业务逻辑代码的时候可能用不到这个特性,一般会封自己的参数检查机制。比如,我在项目中,对于上层 API 开发,我通过封装一个辅助类(ApiGuard)来对对参数进行检查,如果参数不通过,则抛出相应的业务异常,而不是 ArgumentNullException。比如下面的一段截取自我的 GeekGist 小项目的代码:
public static class ApiGuard
{
    public static void EnsureNotNull(object param, string paramName)
    {
        if (param == null) throw new BadRequestException($"{paramName} can not be null.");
    }
    public static void EnsureNotEmpty<T>(IEnumerable<T> collection, string paramName)
    {
        if (collection == null || collection.Count() == 0)
            throw new BadRequestException($"{paramName} can not be null or empty.");
    }
    public static void EnsureExist(object value, string message = "Not found")
    {
        if (value == null) throw new BadRequestException(message);
    }
    public static void EnsureNotExist(object value, string message = "Already existed")
    {
        if (value != null) throw new BadRequestException(message);
    }
    ...
}
使用示例:
public async Task UpdateAsync(long id, BookUpdateDto dto)
{
    ApiGuard.EnsureNotNull(dto, nameof(dto));
    ApiGuard.EnsureNotEmpty(dto.TagValues, nameof(dto.TagValues));
    var book = await DbSet
        .Include(x => x.BookTags)
        .FirstOrDefaultAsync(x => x.Id == id);
    ApiGuard.EnsureExist(book);
    Mapper.Map(dto, book);
    ...
}
ApiGuard 的好处是,当 API 接口接到不合要求的参数时,可以自定义响应返回内容。比如,增加一个 Filter 或中间件用来全局捕获业务代码异常,根据不同的异常返回给前端不同的状态码和消息提示:
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
    ApiResult result;
    if (exception is BadRequestException)
    {
        result = ApiResult.Error(exception.Message, 400);
    }
    else if (exception is NotFoundException)
    {
        message = string.IsNullOrEmpty(message) ? "Not Found" : message;
        result = ApiResult.Error(message, 404);
    }
    else if (exception is UnauthorizedAccessException)
    {
        message = string.IsNullOrEmpty(message) ? "Unauthorized" : message;
        result = ApiResult.Error(message, 401);
    }
    ...
}
只是一个参数非空检查,在实际开发中却有不少的学问,所以学好了理论还要多实践才能更透彻的理解它。
C# 9.0 新特性之参数非空检查简化的更多相关文章
- C#4.0新特性:可选参数,命名参数,Dynamic
		1.可选参数 可以为方法的参数设置一个默认值,如下: class Program { static void Main(string[] args) { Show(); Show("cary ... 
- JAVA8新特性Optional,非空判断
		Optional java 的 NPE(Null Pointer Exception)所谓的空指针异常搞的头昏脑涨, 有大佬说过 "防止 NPE,是程序员的基本修养." 但是修养归 ... 
- EF6.0新特性-DbCommandInterceptor实现非SQL端读写分离
		前几天看了一个基于sqlserver的负载均衡与读写分离的软件Moebius,实现的方式还是不错的,这使得用sqlserver数据库的同学时有机会对数据库进行更有效的优化了 
- C# 9.0 新特性预览 - 空参数校验
		C# 9.0 新特性预览 - 空参数校验 前言 随着 .NET 5 发布日期的日益临近,其对应的 C# 新版本已确定为 C# 9.0,其中新增加的特性(或语法糖)也已基本锁定,本系列文章将向大家展示它 ... 
- C# 7.0 新特性4: 返回引用
		本文参考Roslyn项目中的Issue:#118. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# 7.0 新特性3: 模式匹配 ... 
- 返璞归真 asp.net mvc (8) - asp.net mvc 3.0 新特性之 Model
		原文:返璞归真 asp.net mvc (8) - asp.net mvc 3.0 新特性之 Model [索引页][源码下载] 返璞归真 asp.net mvc (8) - asp.net mvc ... 
- ASP.NET Web API 2.0新特性:Attribute Routing1
		ASP.NET Web API 2.0新特性:Attribute Routing[上篇] 对于一个针对ASP.NET Web API的调用请求来说,请求的URL和对应的HTTP方法的组合最终决定了目标 ... 
- webpack 4.0.0-beta.0 新特性介绍
		webpack 可以看做是模块打包机.它做的事情是:分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式 ... 
- Day07 jdk5.0新特性&Junit&反射
		day07总结 今日内容 MyEclipse安装与使用 JUnit使用 泛型 1.5新特性 自动装箱拆箱 增强for 静态导入 可变参数方法 枚举 反射 MyEclipse安装与使用(yes) 安装M ... 
随机推荐
- 为什么Tableviewcell创建时可以不判空
			dequeueReuseableCellWithIdentifier:与dequeueReuseableCellWithIdentifier:forIndexPath:的区别: 前者不必向tableV ... 
- iOS开发常用技能点(持续更新中。。。)
			1,以屏幕原点开始布局 (默认从导航栏原点布局) self.extendedLayoutIncludesOpaqueBars = YES; 2,向button发送点击事件 [self.playB ... 
- poj3613 求经过n条边的最短路 ----矩阵玩出新高度 。
			For their physical fitness program, N (2 ≤ N ≤ 1,000,000) cows have decided to run a relay race usin ... 
- hdu2093 考试排名(还需完善)
			下面代码是借鉴的.好多的知识点等着完善 #include <iostream> #include <string> #include <algorithm> usi ... 
- 01 . RabbitMQ简介及部署
			RabbitMQ简介  MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它 ... 
- Java实现 LeetCode 700 二叉搜索树中的搜索(遍历树)
			700. 二叉搜索树中的搜索 给定二叉搜索树(BST)的根节点和一个值. 你需要在BST中找到节点值等于给定值的节点. 返回以该节点为根的子树. 如果节点不存在,则返回 NULL. 例如, 给定二叉搜 ... 
- Java实现 LeetCode 662 二叉树最大宽度(递归)
			662. 二叉树最大宽度 给定一个二叉树,编写一个函数来获取这个树的最大宽度.树的宽度是所有层中的最大宽度.这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空. 每一层的 ... 
- Java实现 蓝桥杯 算法提高 01背包
			算法提高 01背包 时间限制:1.0s 内存限制:256.0MB 问题描述 给定N个物品,每个物品有一个重量W和一个价值V.你有一个能装M重量的背包.问怎么装使得所装价值最大.每个物品只有一个. 输入 ... 
- Java实现 LeetCode 402 移掉K位数字
			402. 移掉K位数字 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小. 注意: num 的长度小于 10002 且 ≥ k. num 不会包含任何前导零. 示 ... 
- Java实现 LeetCode 222 完全二叉树的节点个数
			222. 完全二叉树的节点个数 给出一个完全二叉树,求出该树的节点个数. 说明: 完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集 ... 
