Guard 可用于以简化的方式验证方法参数,与手动写入检查和引发异常相比,该方法速度更快、更简洁、更具表现力且不易出错。
工作原理
生成这些 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 | 断言输入任务未取消 |
示例
可以在单元测试中查找更多示例。