1. 程式人生 > 程式設計 >淺談C#9.0新特性之引數非空檢查簡化

淺談C#9.0新特性之引數非空檢查簡化

引數非空檢查是縮寫類庫很常見的操作,在一個方法中要求引數不能為空,否則丟擲相應的異常。比如:

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,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#9.0 引數非空檢查 內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!

作者:王亮
出處:http://cnblogs.com/willick
聯絡:[email protected]