两个方法参数之间的泛型类型参数中的可空性不匹配

时间:2019-11-13 21:56:35

标签: c# generics .net-core nullable-reference-types

我编写了以下扩展方法:

// using System.Collections.Generic;

internal static class TExtensions {
    internal static bool In<T>(this T val, HashSet<T> hs) => hs.Contains(val);
}

并尝试按以下方式使用它:

var s = DateTime.Now.Hour < 15 ? "abcd" : null;
var hs = new HashSet<string>();
Console.WriteLine(s.In(hs));

编译器在最后一行给我警告:

  

CS8620 由于'bool TExtensions.In'中的'hashSet'类型的参数'hs'在'bool TExtensions.In'(字符串?val,HashSet?hs)'中的差异,不能使用参数'HashSet'引用类型的可空性。

因为编译器将T类型的参数解析为sstring?的类型;但是哈希集不是HashSet<T>,而是可以为空的stringHashSet<string?>)的哈希集,而是不是不可为空的string({{1} }。

我可以通过包装空检查来解决此问题:

HashSet<string>

或将哈希集明确键入为具有可为空的元素:

if (s is { }) {
    var result = s.In(hs);
}

但是有什么方法可以使用nullable attributes来允许这种情况吗?还是我可以在var hs = new HashSet<string?>(); 扩展方法中进行其他更改?

1 个答案:

答案 0 :(得分:0)

您可以将[AllowNull]属性用于In方法的第一个参数:

internal static bool In<T>([AllowNull] this T val, HashSet<T> hs) => hs.Contains(val);

但是,正如您在问题中所描述的那样,此处的实际问题是推断的泛型类型参数由s确定。 In方法将被称为In<string?>,该方法要求哈希集为HashSet<string?>。您必须告诉编译器使用string而不是string?s.In<string>(hs)

如果可以约束非空引用类型,则可以使用以下方法:

internal static bool In<T>(this T? val, HashSet<T> hs) where T : class => val != null && hs.Contains(val);

对于值类型,实现为:

internal static bool In<T>(this T? val, HashSet<T> hs) where T : struct => val.HasValue && hs.Contains(val.Value);

请注意,如果type参数本身可以为空,则只有第一个版本(带有[AllowNull]属性)才有效。

您当然可以只使用null-forgiving-operator来关闭编译器:s.In(hs!)s!.In(hs)