【问题标题】:How to specify that nullable reference type (return) is not nullable if argument is true如果参数为真,如何指定可空引用类型(返回)不可为空
【发布时间】:2022-11-04 18:42:17
【问题描述】:

我在可为空的上下文中有这个 C# 代码:

public string? GetValue(int key, bool errorIfInvalidKey)
{
    string result = <get the value identified by the key argument, or null if not found>;
    if (result == null && errorIfInvalidKey) {
        throw new InvalidOperationException("Bad key");
    } else {
      return result;
    }
}

如果调用者指定了无效的key,则errorIfInvalidKey 参数指定是返回null 还是抛出异常。所以,如果errorIfInvalidKey 为真,则此代码保证返回非空.

有没有办法注释此代码以告诉编译器如果参数包含特定值,则返回可能为空的例程将返回非空?

【问题讨论】:

  • 我不这么认为。我仍然不喜欢可空引用类型的部分原因。
  • 引用类型不需要使用可为空的。
  • @TimChang Nullable 引用类型是 C# 8 及更高版本中的新功能,它允许编译器在代码可能访问 null 引用时警告我们。
  • @NineBerry 谢谢我明白了,这个功能让我大开眼界......

标签: c# nullable-reference-types


【解决方案1】:

编译器在方法边界处停止。方法的签名是编译器依赖的契约。例如,如果方法及其调用者位于两个不同的程序集中,则函数的实现可能会发生变化,而调用者不会意识到当程序集被不同版本替换时。这就是为什么编译器必须依赖方法的签名才能正确而不查看其实现以获取更多信息的原因。

当行为差异如此根本不同时(返回 null 或抛出异常),请使用该方法的两个不同版本,而不是尝试通过参数来控制它。

调用该方法的两种不同变体的代码和编写该代码的程序员必须事先知道会发生什么(异常或空值),因此它已经可以决定使用哪种方法。

public string? GetValueOrNull(int key)
{
    string? result = <get the value identified by the key argument, or null if not found>;
    return result;
}

public string GetValue(int key)
{
    string? result = <get the value identified by the key argument, or null if not found>;
    if (result == null) {
        throw new InvalidOperationException("Bad key");
    } else {
      return result;
    }
}

有许多属性可用于向编译器声明方法的内部逻辑如何工作,以便编译器可以根据其他一些条件对变量是否为空做出假设:

Attributes for null-state static analysis interpreted by the C# compiler

但是,它们都不支持您的方案。但是您可以使用 NotNullWhen 属性将上面的 GetValueOrNull 方法替换为遵循 TryGet... 模式的方法:

public bool TryGetValue(int key, [NotNullWhen(true)] out string? value)
{
    value = <get the value identified by the key argument, or null if not found>;
    return value != null;
}

然后可以这样使用:

public void TestSomething() 
{
    if(TryGetValue(42, out string? value))
    {
        // No warning here
        Console.WriteLine(value.Length);
    }
    
    // Warning: Dereference of a possibly null reference
    Console.WriteLine(value.Length);
}

【讨论】:

  • 很好的信息,但它没有回答 OP 中的是/否问题。
  • @Bob.at.Indigo.Health 我已经扩展了答案。
【解决方案2】:

只需删除问号。如果函数完全返回,则编译器根据您在此处的代码知道它实际上不能为空。它会看到您的空检查引发异常。

【讨论】:

  • 方法可以返回null 值。
猜你喜欢
  • 2017-01-18
  • 1970-01-01
  • 1970-01-01
  • 2019-06-02
  • 2019-12-11
  • 2020-09-16
  • 2019-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多