4

I have an extension method like this:

[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!;
}

This works fine. Were I to call it expecting a non-null value, the compiler warns me the results could be null, which is exactly what I want.

However, if I have another method that calls GetValueOrDefault, and I forget to add the [return: MaybeNull], the compiler won't warn me at all. Here's a convoluted example just to explain my issue:

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!

I'm pretty new to C# 8.0 nullable reference types, especially involving generics. Is there anything I'm missing to get this to work? Without the compiler warning, it feels like it defeats the purpose of using C# 8.0 nullable types. I'm lulled into a false sense of security that I couldn't possibly miss a NullReferenceException, especially when Visual Studio reassures me "s2 is not null here" even though it absolutely is.

Ber'Zophus
  • 7,497
  • 3
  • 22
  • 22
  • I think, that the reason of that `TKey` and `TValue` can be reference of value type in your example. And compiler simply doesn't know, whether type itself can be in runtime – Pavel Anikhouski Jan 29 '20 at 15:12
  • It's true; if I used `where TValue : class` instead, and made the return type `TValue?`, the compiler will warn me fine. But TValue won't always be a class; I sometimes have value types on my IDictionary. – Ber'Zophus Jan 29 '20 at 15:24
  • You can refer to this [document](https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/), especially to _The `notnull` generic constraint_ and _issue with `T?`_ parts – Pavel Anikhouski Jan 29 '20 at 15:25
  • You can also have a look at existing threads, like [this](https://stackoverflow.com/questions/54593923/nullable-reference-types-with-generic-return-type) or [this](https://stackoverflow.com/questions/55975211/nullable-reference-types-how-to-specify-t-type-without-constraining-to-class). It seems, that `MaybeNull` is the best option here, and you should use to help the compiler to identify the correct return type – Pavel Anikhouski Jan 29 '20 at 15:45

1 Answers1

0

This has been improved in newer versions of the compiler (still C# 8, just a newer compiler). If you use a newer Visual Studio, you will have a newer compiler.

Here is your example:

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

There are two improvements:

  1. You don't need to ! the default as the analysis now respects the MaybeNull attribute on GetValueOrDefault.
  2. You now get warning CS8603 (Possible null reference return) where you were previously pointing out that you didn't get a warning but rightly should.
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742