从另一个泛型方法调用 MaybeNull 泛型方法

Calling a MaybeNull generic method from another generic method

我有这样的扩展方法:

[return: MaybeNull]
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
where TKey : notnull
where TValue : notnull {
    if (dictionary.TryGetValue(key, out TValue value)) return value;
    else return default!;
}

这很好用。如果我调用它期望一个非空值,编译器会警告我结果可能为空,这正是我想要的。

但是,如果我有另一个调用 GetValueOrDefault 的方法,而我忘记添加 [return: MaybeNull],编译器根本不会警告我。这是一个复杂的例子来解释我的问题:

public static TValue SomeOtherMethod<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
where TKey : notnull
where TValue : notnull
    => dictionary.GetValueOrDefault(key); // No warning this could be null

...

var dictionary = new Dictionary<string, string>() {
    ["hello"] = "world"
};
string s1 = dictionary.GetValueOrDefault("foo"); // Compiler warning
string s2 = dictionary.SomeOtherMethod("foo"); // No compiler warning
int s2Len = s2.Length; // Visual Studio states "s2 is not null here", even though it absolutely is!

我对 C# 8.0 可为 null 的引用类型还很陌生,尤其是涉及泛型时。有什么我想念的吗?没有编译器警告,感觉它违背了使用 C# 8.0 可空类型的目的。我陷入了一种错误的安全感,我不可能错过 NullReferenceException,尤其是当 Visual Studio 让我放心 "s2 is not null here" 时,尽管它绝对是。

这在较新版本的编译器中得到了改进(仍然是 C# 8,只是较新的编译器)。如果您使用更新的 Visual Studio,您将拥有更新的编译器。

这是你的例子:

https://sharplab.io/#v2:EYLgtghgzgLgpgJwD4GIB2BXANliwtwAEcaeBAsAFBUACADITQIwAsA3LQ8wHQAiAlhADmaAPax+AYyjcAwqIAmcAIKksATyj8oHapRoBmRkwBsjAEyFZhAN5VCDxkeZmaLQgFkAFAEpb9xwBfAIcQwgBtGgB2EE8IdWA4ADlsLABdMMNjMwAVADUILAwiAHE4GAKiuAB5BF44ADMIbBgAHhyAaTh1ABpCfMLigD4vGAALbUIASQFJGH5RUgR1dq7e/srhwgUpecWIZb7O7sIAa26fMMdCAHcxxCJj9UJYsRhMHCvHO4eNwaJXqJ3qkwnZKNdrvwGoQvDs5gslupuDllmUKv8vOd1qIMDA/lVCAA3f4+S7giEU6JE/66CkOOBYKBwL6UqLbRrNLAwWlBMKZZymfHFQgAZVEYBq40QHnKY0Uq26R02cBG40mM12CIOKyeSv+Q22mv2h36azOFxZt3uCEeZsBwM+5IpPxtQoBhDeHywYQAvAa4XtEdw0cravUmi1MRc2IQAPSxwhJUS3A5ofhoISENVQQiSHFYBSERIekGUQJAA===

有两个改进:

  1. 您不需要 ! default,因为分析现在尊重 GetValueOrDefault 上的 MaybeNull 属性。
  2. 您现在收到警告 CS8603(可能为空引用 return),您之前指出您没有收到警告但理应收到警告。