WhereNotNull in LINQ

I'm a big fan of the Nullable Reference Types feature added in C# 8.0. It lets the compiler catch a bunch of potential runtime errors, and I guarantee if you turn it on you'll get a bunch of warnings. As of .NET 5.0, the entire BCL is now covered with nullable annotations, and a lot of the extended ecosystem supports them too.

But there are situations where the compiler is unable to work out that you've done a null check, meaning you either have to use the "null-forgiving operator" (where you append a bang to the identifier, like item!.Name), disable the null checking, or just ignore the warning.

One of these that I run across a lot is in LINQ chains. For example, I'm just writing some code that enumerates through the MetadataReferences property of a Roslyn Project to find the file paths of references:

var files = project.MetadataReferences
    .OfType<PortableExecutableReference>
    .Select(p => p.FilePath);

In the Roslyn library, PortableExecutableReference.FilePath is declared as string?, because apparently you can have a PortableExecutableReference that isn't actually a file. So the type of this expression is IEnumerable<string?>.

Now, I can filter out the null file paths with a simple Where call:

var files = project.MetadataReferences
    .OfType<PortableExecutableReference>
    .Select(p => p.FilePath)
    .Where(f => f is not null);

But the compiler isn't actually clever enough to work out what that Where does, so the type of the expression is still IEnumerable<string?>. Vexing. There's actually no easy way to deal with this within a chain of LINQ methods (that I can think of, at least).

So, I've added an extension method to my common library of "stuff that's not in the BCL" that filters out nulls and changes the expression type accordingly.

public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class
{
    foreach (var item in source)
    {
        if (item is not null) yield return item;
    }
}

And I thought that might be useful for other people so I'm sharing it here.

I ran into this while working on Visual ReCode. It's an extension for Visual Studio 2019 that helps you migrate code from .NET 4.x to .NET Core 3.1 and .NET 5.0, including a WCF to gRPC conversion to upgrade your services to the modern, cross-platform standard for RPC.

Previous
Previous

Running a .NET 5 web app on Railway

Next
Next

Creating a dotnet tool