测试小站: 处理网 回收帮 培训网 富贵论坛 老富贵论坛

C# 9.0 新特性之简化参数非空检查

  阅读本文大概需要 1 分钟。

  参数非空检查是编写类库很常见的操作,在一个方法中要求参数不能为空,否则抛出相应的异常。比如:

  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(IEnumerable 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 DistinctBy<TSource, TKey>(

  this IEnumerable source,

  Func<TSource, TKey> keySelector)

  {

  Guard.NotNullOrEmpty(source, nameof(source));

  ...

  }

  介于这种非空检查极其常见,C# 9.0 对此做了简化,增加了操作符‘!’,放在参数名后面,表示此参数不接受 null 值。使用方式如下:

  public static string HashPassword(string password!)

  {

  ...

  }

  简化了很多有木有。这个提案已经纳入 C# 9.0 的特性中,但目前(2021-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(IEnumerable 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);

  }

  ...

  }

  只是一个参数非空检查,在实际开发中却有不少的学问,所以学好了理论还要多实践才能更透彻的理解它。

posted @ 2021-12-19 21:27  linjingyg  阅读(333)  评论(0)    收藏  举报