如何指定可空引用类型(返回值)在参数为true时不可为空?

3
我在可空上下文中有以下这段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;
    }
}

如果调用者指定了无效的keyerrorIfInvalidKey参数指定是返回null还是抛出异常。因此,如果errorIfInvalidKey为真,则保证该代码将返回非null值

有没有一种方法可以注释这段代码,告诉编译器如果一个参数包含特定的值,则返回可能为null的例程将返回非null值?


1
我不这么认为。其中一部分原因是我仍然不喜欢可空引用类型。 - José Ramírez
引用类型不需要使用可空。 - TimChang
@TimChang 可空引用类型是 C# 8 及更高版本的新功能,它允许编译器在代码可能访问 null 引用时发出警告。 - NineBerry
@NineBerry谢谢,我明白了,这个功能开阔了我的思路... - TimChang
2个回答

1
编译器会在方法边界处停止。方法的签名是编译器所依赖的合同。例如,如果方法和它的调用方位于两个不同的程序集中,那么当程序集被替换为不同的版本时,函数的实现可能会发生变化,而调用方却没有意识到。这就是为什么编译器必须依赖方法的签名而不查看其实现以获取更多信息的原因。
当行为差异如此根本不同(返回null或抛出异常)时,请使用两个不同版本的方法,而不是通过参数来控制它。
调用两个不同变体的方法的代码以及编写该代码的程序员将不得不事先知道要期望什么(异常或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;
    }
}

有许多属性可以用来声明方法的内部逻辑,以便编译器可以根据其他条件推断变量是否为空:

用于C#编译器解释的null-state静态分析属性

然而,这些属性都不支持您的情况。但是,您可以使用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);
}

好的信息,但它没有回答原帖中的是/否问题。 - Bob.at.Indigo.Health
@Bob.at.Indigo.Health 我已经扩展了答案。 - NineBerry

1
有没有一种方法可以在代码中注释告诉编译器,如果参数包含特定值,则返回可能为空的例程将返回非空值?
目前还没有这样的注释方式。最接近的是DoesNotReturnIfAttribute,但它会在调用具有相应值的方法后停止所有的空检查。例如:
string? GetValue(int key, [DoesNotReturnIf(true)] bool errorIfInvalidKey)
{
    // ...
}

var v1 = GetValue(2, false);
Console.WriteLine(v1.GetHashCode()); // warning
var v2 = GetValue(1, true);
Console.WriteLine(v2.GetHashCode()); // no warning
Console.WriteLine(v1.GetHashCode()); // no warning !!!

我认为将方法分成两部分不仅可以解决问题,还可以提高代码的可读性。
string? GetValue(int key)
{
    // ...
} 

string GetValueOrThrow(int key) => GetValue(key) ?? new InvalidOperationException("Bad key");

使用方法更加明显 - GetValueOrThrow(1) 读起来比 GetValue(1, true) 更好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接