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:
nonNullItems
is inferred as IEnumerable<string>
.item!
(because I want to benefit from the compilers sanity checking, and not rely on me being an error-free coder)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?
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.
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)!;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With