外部函数作为可空守卫的可能性?

Possibility of external functions as nullable guards?

C# 8 引入了可空引用类型,这是一个非常酷的特性。现在,如果您希望获得可为空的值,则必须编写所谓的守卫:

object? value = null;
if (value is null)
{
  throw new ArgumentNullException();
}
…

这些可能有点重复。我想知道的是是否有可能避免为每个变量编写这种类型的代码,而是有一个 guard-type static void 函数,如果值为 null 或只是 [=19= 则抛出异常] 如果值不是 null。或者这对编译器来说太难推断了吗?特别是如果它是外部的 library/package?

您可以做一些事情。

你可以在你的守卫方法中使用 [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
}

这个works because:

[DoesNotReturnIf(bool)]: Placed on a bool parameter. Code after the call is unreachable if the parameter has the specified bool value


或者,您可以像这样声明一个保护方法:

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
}

这个works because:

[NotNull]: For outputs (ref/out parameters, return values), the output will not be null, even if the type allows it. For inputs (by-value/in parameters) the value passed is known not to be null when we return.

SharpLab with examples


当然,真正的问题是为什么你想这样做。如果您不希望 value 成为 null,则将其声明为 object?,而不是 object——这就是 NRT 的意义所在。

我认为 Guard Clauses library by Steve Ardalis 可以帮助您解决这种情况。 您可以执行以下操作:

  • Guard.Against.Null(如果输入为空则抛出)
  • Guard.Against.NullOrEmpty(如果字符串或数组输入为 null 或空则抛出)
  • Guard.Against.NullOrWhiteSpace(如果字符串输入为 null、空或空白则抛出)
  • Guard.Against.OutOfRange(如果 integer/DateTime/enum 输入超出提供的范围则抛出)
  • Guard.Against.OutOfSQLDateRange(如果 DateTime 输入超出 SQL 服务器日期时间值的有效范围则抛出)
  • Guard.Against.Zero(如果数字输入为零则抛出)

在此blog post Jason Roberts中也对库进行了快速解释。

Microsoft.Toolkit.Diagnostics 命名空间中还有另一个 Guard Class,但可能在所有用例中都不可行,这取决于是否要将该依赖项添加到项目与否。