微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 .NET 5 SDK

如何解决使用 .NET 5 SDK

我在 IDictionary 上实现了以下扩展方法,它将尝试从字典中获取一个值,但如果键没有返回认值(default(T)用户提供的)'存在。没有用户提供值的第一个方法将通过 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,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;
}

使用 .NET SDK 3.1.100,此代码构建良好。但是,使用最新的 .NET SDK 5.0.101,我收到以下错误消息:

错误 CS8620:'IDictionary' 类型的参数不能用于 'T? DictionaryExtensions.GetValueOrDefault(IDictionary source,T? defaultValue)' 由于引用类型的可空性不同。

它抱怨在 default 中使用了 GetValueOrDefault(source,defaultValue: default)。使用 default! 当然会抑制错误消息,但该值应该可以为空(因此 AllowNullAttribute 上的 defaultValue)。或者可能是因为属性用法而推断 T 可以为 null,并且不允许使用不可为 null 的 T 进行调用

错误仅在 T 为泛型且不限于 class 时产生。例如,以下代码不会产生错误

var dict = new Dictionary<string,string>();
dict.GetValueOrDefault("key",null);

我做错了吗?新的 .NET 版本是否进一步收紧了对可为空引用类型的约束?这仅仅是 .NET SDK 5.0.101 的一个错误吗?

解决方法

在这种情况下,我认为可空性分析得到了改进。

[AllowNull] 和朋友不会影响编译器对泛型类型参数的推断。这里似乎发生的是编译器正在查看对 GetValueOrDefault(source,key,defaultValue: default) 的调用并试图推断 TKeyT 是什么。因为您将 default 作为 T 的值传递(忽略 [AllowNull]),所以它意识到赋予 TGetValueOrDefault 必须可以为空,即它正在调用 GetValueOrDefault<TKey,T?>(source,defaultValue: default)

但是,它也意识到 source 可能是 IDictionary<TKey,T>(因此 T 不可为空),并意识到这里存在冲突。

这都是学术性的,如 C# 9 introduced the T? syntax。这比添加属性要简洁得多,支持 Task<T?> 之类的东西,并且可以更好地与编译器集成。

这如您所愿:

public static T? GetValueOrDefault<TKey,T>(this IDictionary<TKey,T> source,TKey key) where TKey : notnull
{
    return GetValueOrDefault(source,defaultValue: default);
}

public static T? GetValueOrDefault<TKey,TKey key,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;
}

这里编译器仍然注意到您正在传递 default,但它意识到 defaultValue 具有类型 T? 而不是 T(它忽略了 { {1}} 属性之前),因此不会强制 [AllowNull] 为可空。


如果您坚持使用 C# 8,似乎明确指定泛型类型参数会阻止编译器将 T 推断为 T,从而消除警告:

T?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。