Eam*_*nne 6 c# nullable nullable-reference-types
我有这样的代码:
IEnumerable<string?> items = new [] { "test", null, "this" };
var nonNullItems = items.Where(item => item != null); //inferred as IEnumerable<string?>
var lengths = nonNullItems.Select(item => item.Length); //nullability warning here
Console.WriteLine(lengths.Max());
Run Code Online (Sandbox Code Playgroud)
我如何以一种方便的方式编写此代码,例如:
nonNullItems推断为IEnumerable<string>。item!(因为我想从编译器的健全性检查中受益,而不是依靠我成为无错误的编码器)我知道这种解决方案,它利用了C#8.0编译器中的流敏感型输入,但是它不是很漂亮,主要是因为它又长又嘈杂:
var notNullItems = items.SelectMany(item =>
item != null ? new[] { item } : Array.Empty<string>())
);
Run Code Online (Sandbox Code Playgroud)
有更好的选择吗?
我认为您必须以一种或另一种方式帮助编译器。调用.Where()永远不会安全返回非空。也许微软可以添加一些逻辑来确定像你这样的基本场景,但 AFAIK 现在不是这种情况。
但是,您可以编写一个简单的扩展方法,如下所示:
public static class Extension
{
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> o) where T:class
{
return o.Where(x => x != null)!;
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,您将不得不告诉编译器您对情况的了解要多于实际情况。
一个原因是该Where方法的注释方式没有使编译器理解对非空性的保证,也实际上没有可能对其进行注释。可能需要在编译器中添加其他启发式方法,以了解一些基本情况,例如这种情况,但目前我们还没有。
因此,一种选择是使用原谅为null的运算符,俗称“ dammit运算符”。不过,您可以自己动手操作,而不是在使用该集合的代码上撒满感叹号,而可以在制作该集合的过程中附加一个步骤,至少对我来说,这使它更可口:
var nonNullItems = items.Where(item => item != null).Select(s => s!);
Run Code Online (Sandbox Code Playgroud)
这将标记nonNullItems为,IEnumerable<string>而不是IEnumerable<string?>,因此可以在其余代码中正确处理。
我不知道这个答案是否符合您的第三个要点的标准,但您的.Where()过滤器也不符合,所以......
代替
var nonNullItems = items.Where(item => item != null)
和
var nonNullItems = items.OfType<string>()
IEnumerable<string>这将产生for的推断类型nonNullItems,并且此技术可以应用于任何可为空的引用类型。