防护

Guard 可用于以简化的方式验证方法参数,与手动写入检查和引发异常相比,该方法速度更快、更简洁、更具表现力且不易出错。

平台 API:GuardCallerArgumentExpressionAttribute

工作原理

生成这些 Guard API 时要考虑的三个核心原则:

  • 速度快。 为了达到此目的,所有 API 都设计为在调用方尽可能少地生成代码,并且每个 Guard API(几乎总是)会内联。 此外,还使用 T4 模板生成专用方法,以在处理常见类型(例如基元数值类型)时获得最有效的程序集代码。
  • 有帮助。 每个 Guard API 都会在适用时生成一条详细的异常消息,明确指出出现的问题以及其他信息(例如当前变量值)。
  • 直观。 为了实现这一点,所有 Guard API 都有富有表现力且显而易见的名称,使每个 API 应该执行何种操作一目了然。 这避免了开发人员编写检查时的负担,使其可以使用自然语言表达条件。

语法

下面是一个示例方法,其中一些检查是使用显式和手动引发语句完成的:

public static void SampleMethod(int[] array, int index, Span<int> span, string text)
{
    if (array is null)
    {
        throw new ArgumentNullException(nameof(array), "The array must not be null");
    }

    if (array.Length >= 10)
    {
        throw new ArgumentException($"The array must have less than 10 items, had a size of {array.Length}", nameof(array));
    }

    if (index < 0 || index >= array.Length)
    {
        throw new ArgumentOutOfRangeException(nameof(index), $"The index must be in the [0, {array.Length}) range, was {index}");
    }

    if (span.Length < array.Length)
    {
        throw new ArgumentException($"The target span is shorter than the input array, had a length of {span.Length}", nameof(span));
    }

    if (string.IsNullOrEmpty(text))
    {
        throw new ArgumentException("The input text can't be null or empty", nameof(text));
    }
}

下面是相同的方法,但使用新增的 Guard.APIs 来验证输入参数:

public static void SampleMethod(int[] array, int index, Span<int> span, string text)
{
    Guard.IsNotNull(array);
    Guard.HasSizeGreaterThanOrEqualTo(array, 10);
    Guard.IsInRangeFor(index, array);
    Guard.HasSizeLessThanOrEqualTo(array, span);
    Guard.IsNotNullOrEmpty(text);
}

Guard API 将以尽可能快的方式执行所需的检查,并在失败时引发的相应异常,同时显示格式良好的消息。

注意

Guard API 依赖于 [CallerArgumentExpression] 自动推断要验证的参数的名称。 这要求在正在使用的项目中启用 C# 10。 如果使用的是较低版本的语言,则需要手动传递参数名称,例如,使用 nameof() 来引用它(例如 Guard.IsNotNull(array, nameof(array))))。

方法

Guard 类中有数十个不同的 API 和重载,下面是其中一些:

常规

方法 返回类型 说明
IsNotNull<T>(T, string) void 确定输入值不为 null
IsOfType<T>(object, string) void 确定输入值为特定类型
IsAssignableToType<T>(object, string) void 确定可将输入值分配给指定类型
IsReferenceEqualTo<T>(T, T, string) void 确定输入值必须与目标值为相同的实例
IsTrue(bool, string) void 确定输入值必须为 true

比较

方法 返回类型 说明
IsEqualTo<T>(T, T, string) void 断言输入值必须等于指定值
IsBitwiseEqualTo<T>(T, T, string) void 断言输入值必须与指定值按位匹配
IsLessThan<T>(T, T, string) void 断言输入值必须小于指定值
IsLessThanOrEqualTo<T>(T, T, string) void 断言输入值必须小于或等于指定值
IsInRange<T>(T, T, T, string) void 断言输入值必须位于 [minimum, maximum) 范围内
IsBetween<T>(T, T, T, string name) void 断言输入值必须位于 (minimum, maximum) 间隔内
IsBetweenOrEqualTo<T>(T, T, T, string name) void 断言输入值必须位于 [minimum, maximum] 间隔内

字符串

方法 返回类型 说明
IsNotNullOrEmpty(string, string) void 断言输入字符串实例不得为 null 或为空
IsNotNullOrWhitespace(string, string) void 断言输入字符串实例不得为 null 或空格

集合

方法 返回类型 说明
IsNotEmpty<T>(T[], string) void 断言输入数组实例不得为空
HasSizeEqualTo<T>(T[], int, string) void 断言输入数组实例必须具有指定值的大小
HasSizeAtLeast<T>(T[], int, string) void 断言输入数组的大小必须小于或等于指定值
IsInRangeFor<T>(int, T[], string) void 断言输入索引对给定数组有效
HasSizeLessThanOrEqualTo<T>(T[], T[], string) void 断言源数组的大小必须小于或等于目标数组的大小

任务

方法 返回类型 说明
IsCompleted(Task, string) void 断言输入任务处于已完成状态
IsNotCanceled(Task, string) void 断言输入任务未取消

示例

可以在单元测试中查找更多示例。