I have implemented the following extension methods on IDictionary
, which will try to get a value from a dictionary, but return a default value (either default(T)
or user-provided) if the key doesn't exist. The first method without a user-provided value will call through to the other method with default
.
[return: MaybeNull]
public static T GetValueOrDefault<TKey, T>(this IDictionary<TKey, T> source, TKey key) where TKey : notnull
{
return GetValueOrDefault(source, key, defaultValue: default);
}
[return: MaybeNull]
public static T GetValueOrDefault<TKey, T>(this IDictionary<TKey, T> source, TKey key, [AllowNull] T defaultValue) where TKey : notnull
{
if (source is null) throw new ArgumentNullException(nameof(source));
if (key is null) throw new ArgumentNullException(nameof(key));
if (source.TryGetValue(key, out var item))
{
return item;
}
return defaultValue;
}
With .NET SDK 3.1.100 this code builds fine. However, with the newest .NET SDK 5.0.101, I get the following error message:
error CS8620: Argument of type 'IDictionary<TKey, T>' cannot be used for parameter 'source' of type 'IDictionary<TKey, T?>' in 'T? DictionaryExtensions.GetValueOrDefault<TKey, T?>(IDictionary<TKey, T?> source, TKey key, T? defaultValue)' due to differences in the nullability of reference types.
It complains about the use of default
in GetValueOrDefault(source, key, defaultValue: default)
. Using default!
suppresses the error message of course, but the value is supposed to be nullable (hence the AllowNullAttribute
on defaultValue
). Or maybe it is deducing that T
is nullable due to the attribute and usage, and won't allow a call with the non-nullable T
?
The error is only produced when T
is generic and not constrained to class
. For instance, the following code does not produce the error:
var dict = new Dictionary<string, string>();
dict.GetValueOrDefault("key", null);
Am I doing something wrong? Has the constraints on nullable reference types been further tightened with the new .NET version? Is this simply a bug with .NET SDK 5.0.101?