我编写了以下扩展方法:
// 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
类型的参数解析为s
或string?
的类型;但是哈希集不是HashSet<T>
,而是可以为空的string
(HashSet<string?>
)的哈希集,而是不是不可为空的string
({{1} }。
我可以通过包装空检查来解决此问题:
HashSet<string>
或将哈希集明确键入为具有可为空的元素:
if (s is { }) {
var result = s.In(hs);
}
但是有什么方法可以使用nullable attributes来允许这种情况吗?还是我可以在var hs = new HashSet<string?>();
扩展方法中进行其他更改?
答案 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)
。