C# 8 引入了可为空的引用类型,这是一个非常酷的功能。现在,如果您希望获得可为空的值,则必须编写所谓的保护:
object? value = null;
if (value is null)
{
throw new ArgumentNullException();
}
…
这些可能有点重复。我想知道的是是否可以避免为每个变量编写这种类型的代码,而是有一个保护类型的static void
函数,如果值null
则抛出异常,或者如果值不null
则返回。还是编译器很难推断?特别是如果是外部库/包?
你可以做一些事情。
您可以在 guard 方法中使用[DoesNotReturnIf(...)]
,以指示在特定条件为真或假时抛出该条件,例如:
public static class Ensure
{
public static void True([DoesNotReturnIf(false)] bool condition)
{
if (!condition)
{
throw new Exception("!!!");
}
}
}
然后:
public void TestMethod(object? o)
{
Ensure.True(o != null);
Console.WriteLine(o.ToString()); // No warning
}
这是有效的,因为:
[DoesNotReturnIf(bool)]
:放置在布尔参数上。如果参数具有指定的布尔值,则无法访问调用后的代码
或者,您可以声明如下保护方法:
public static class Ensure
{
public static void NotNull([NotNull] object? o)
{
if (o is null)
{
throw new Exception("!!!");
}
}
}
并像这样使用它:
public void TestMethod(object? o)
{
Ensure.NotNull(o);
Console.WriteLine(o.ToString()); // No warning
}
这是有效的,因为:
[NotNull]
:对于输出(ref/out 参数、返回值(,即使类型允许,输出也不会为 null。对于输入(按值/in参数(,当我们返回时,传递的值已知不为空。
夏普实验室与示例
当然,真正的问题是你为什么要这样做。如果你不希望value
null
,那么将其声明为object?
,而不是object
- 这就是拥有NRT的意义所在。
史蒂夫·阿达利斯(Steve Ardalis(有一个保护条款库,我认为可以帮助您解决这种情况。 您可以执行以下操作:
Guard.Against.Null
(如果输入为空,则引发(Guard.Against.NullOrEmpty
(如果字符串或数组输入为 null 或为空,则引发(Guard.Against.NullOrWhiteSpace
(如果字符串输入为空、空或空格,则抛出(Guard.Against.OutOfRange
(如果输入超出提供的范围integer/DateTime/enum
则引发(Guard.Against.OutOfSQLDateRange
(如果输入超出 SQL Server 日期时间值的有效范围DateTime
则引发(Guard.Against.Zero
(如果数字输入为零,则抛出(
在这篇博文中,杰森·罗伯茨(Jason Roberts(也对图书馆进行了快速解释。
Microsoft.Toolkit.Diagnostics
命名空间中还有另一个 Guard 类,但在取决于是否要将该依赖项添加到项目中的所有用例中可能并不可行。