Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Linq's Where/Select to filter out null and convert the type to non-nullable cannot be made into an extension method

Suppose I have

List<MyObject?> list = ...;

I want to turn it into List<MyObject>, but I have not been able to drop the nullable reference.

Below is an MCVE. In my project I have nullable reference warnings turned to errors, so the commented out line below will not compile.

If I do .Where(e => e != null).Select(e => e!) then it will be fine in the latest .NET Core 3.1.100, however I cannot extract this into an extension method.

I tried adding this extension method

    public static IEnumerable<T> NotNull<T>(this IEnumerable<T> enumerable)
    {
        return enumerable.Where(e => e != null).Select(e => e!);
    }

However it will not convert IEnumerable<MyObject?> to IEnumerable<MyObject> and I am unsure why. This leads me to an error like:

[CS8619] Nullability of reference types in value of type 'List' doesn't match target type 'List'.

Is there a way I can make the NotNull function above work somehow?

like image 638
Water Avatar asked Dec 20 '19 21:12

Water


People also ask

How to filter null values in a string using where query?

After your Select linq query, filter null values with !string.IsNullOrEmpty ("string") or string.IsNullOrWhiteSpace ("string") in your Where query. Show activity on this post. Use Where Instead of Select (Linq).

Why do I get a nullreferenceexception when I query a collection?

If a source collection is null or contains an element whose value is null, and your query does not handle null values, a NullReferenceException will be thrown when you execute the query.

What are the methods provided by LinQ for filtering?

There are two methods provided by LINQ in C# which are used for filtering. They are as follows In this article, I am going to discuss the “Where” operator in detail. In the next article, I will discuss the OfType operator with some examples.

When to use the where standard query operator in LINQ?

We need to use the where standard query operator in LINQ when we need to filter the data from a data source based on some condition (s) just like as we did in SQL using the where clause. So in simple words, we can say that it is used to filter the data from a data source based on some condition (s).


3 Answers

You have to update your extension method to the following

public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> enumerable) where T : class
{
    return enumerable.Where(e => e != null).Select(e => e!);
}

The point here is that you are converting the IEnumerable of nullable references to not nullable ones, therefore you'll have to use IEnumerable<T?>. where T : class generic constraint is needed to help the compiler distinguish between nullable reference type and Nullable<T> struct, as you can read here

Because of this issue between the concrete representations of nullable reference types and nullable value types, any use of T? must also require you to constrain the T to be either class or struct.

After that the following lines will be compiled without any warnings

var list = new List<MyObject?>();
IEnumerable<MyObject> notNull = list.NotNull();
like image 170
Pavel Anikhouski Avatar answered Dec 03 '22 17:12

Pavel Anikhouski


This question is overlaps a lot with Is there a convenient way to filter a sequence of C# 8.0 nullable references, retaining only non-nulls?

One answer posted there exhibited best performance and was extremely consise, with the relevant coding snippet repeated here for posterity:

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

Notably; you don't need to Select just to remove the ? annotation, and I think it's a pretty reasonable place to place a nullability ! given that it's pretty clearly correct and likely centralized. If you really cared about GC perf you might consider caching the delegate in a static readonly field, though whether that's meaningfully faster is something you'd need to measure.

If you prefer a zero-tolerance approach to non-null claims via !, then the other answer https://stackoverflow.com/a/59434717/42921 is likely as good as it gets.

like image 33
Eamon Nerbonne Avatar answered Dec 03 '22 17:12

Eamon Nerbonne


You can use following extensions for Dotnet Core 3.1 both class and struct types.

    [Pure]
    public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> enumerable) where T : class
    {
        return enumerable.Where(e => e != null).Select(e => e!);
    }

    [Pure]
    public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> enumerable) where T : struct
    {
        return enumerable.Where(e => e.HasValue).Select(e => e!.Value);
    }
like image 44
ertan Avatar answered Dec 03 '22 17:12

ertan