Is there a convenient way to filter a sequence of C# 8.0 nullable references, retaining only non-nulls?

。_饼干妹妹 提交于 2020-06-14 04:08:48

问题


I have code like this:

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());

How can I write this code in a convenient way such that:

  • There is no nullability warning, because the type nonNullItems is inferred as IEnumerable<string>.
  • I don't need to add unchecked non-nullability assertions like item! (because I want to benefit from the compilers sanity checking, and not rely on me being an error-free coder)
  • I don't add runtime checked non-nullability assertions (because that's pointless overhead both in code-size and at runtime, and in case of human error that fails later than ideal).
  • The solution or coding pattern can apply more generally to other sequences of items of nullable-reference type.

I'm aware of this solution, which leverages the flow-sensitive typing in the C# 8.0 compiler, but it's.... not so pretty, mostly because it's so long and noisy:

var notNullItems = items.SelectMany(item => 
    item != null ? new[] { item } : Array.Empty<string>())
);

Is there a better alternative?


回答1:


I think you'll have to help the compiler in either one way or another. Calling .Where() is never safe of returning not-null. Maybe Microsoft could add some logic to determine basic scenarios like yours, but AFAIK that's not the situation right now.

However, you could write a simple extension method like that:

public static class Extension
{
    public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> o) where T:class
    {
        return o.Where(x => x != null)!;
    }
}



回答2:


Unfortunately you will have to tell the compiler that you know more about the situation than it does.

One reason would be that the Where method has not been annotated in a way that lets the compiler understand the guarantee for non-nullability, nor is it actually possible to annotate it. There might be a case for having additional heuristics added to the compiler to understand some basic cases, like this one, but currently we do not have it.

As such, one option would be to use the null forgiving operator, colloquially known as the "dammit operator". You touch upon this yourself, however, instead of sprinkling exclamation marks all over the code where you use the collection, you can instead tuck on an additional step on producing the collection which, at least to me, makes it more palatable:

var nonNullItems = items.Where(item => item != null).Select(s => s!);

This will flag nonNullItems as IEnumerable<string> instead of IEnumerable<string?>, and thus be handled correctly in the rest of your code.




回答3:


I don't know if this answer meets the criteria for your 3rd bullet point, but then your .Where() filter does not either, so...

Replace

var nonNullItems = items.Where(item => item != null)

with

var nonNullItems = items.OfType<string>()

This will yield an inferred type of IEnumerable<string> for nonNullItems, and this technique can be applied to any nullable reference type.



来源:https://stackoverflow.com/questions/58372791/is-there-a-convenient-way-to-filter-a-sequence-of-c-sharp-8-0-nullable-reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!