How to null check c# 7 tuple in LINQ query?

Viewed 29199

Given:

class Program
{
    private static readonly List<(int a, int b, int c)> Map = new List<(int a, int b, int c)>()
    {
        (1, 1, 2),
        (1, 2, 3),
        (2, 2, 4)
    };

    static void Main(string[] args)
    {
        var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4);

        if (result == null)
            Console.WriteLine("Not found");
        else
            Console.WriteLine("Found");
    }
}

In the above example, a compiler error is encountered at line if (result == null).

CS0019 Operator '==' cannot be applied to operands of type '(int a, int b, int c)' and '<null>'

How would I go about checking that the tuple is found prior to proceeding in my "found" logic?

Prior to using the new c# 7 tuples, I would have this:

class Program
{
    private static readonly List<Tuple<int, int, int>> Map = new List<Tuple<int, int, int>>()
    {
        new Tuple<int, int, int> (1, 1, 2),
        new Tuple<int, int, int> (1, 2, 3),
        new Tuple<int, int, int> (2, 2, 4)
    };

    static void Main(string[] args)
    {
        var result = Map.FirstOrDefault(w => w.Item1 == 4 && w.Item2 == 4);

        if (result == null)
            Console.WriteLine("Not found");
        else
            Console.WriteLine("Found");
    }
}

Which worked fine. I like the more easily interpreted intention of the new syntax, but am unsure on how to null check it prior to acting on what was found (or not).

10 Answers

In C# 7.3, it's very clean:

var result = Map.FirstOrDefault(w => w.a == 4 && w.b == 4);
if (result == default) {
    Console.WriteLine("Not found");
} else {
    Console.WriteLine("Found");
}

You need:

if (result.Equals(default)) Console.WriteLine(...

(c# > 7.1)

how i did it with c# 7.3

T findme;
var tuple = list.Select((x, i) => (Item: x, Index: i)).FirstOrDefault(x => x.Item.GetHashCode() == findme.GetHashCode());

if (tuple.Equals(default))
    return;

...
var index = tuple.Index;

Most of the answers above imply that your resulting element cannot be default(T), where T is your class/tuple.

A simple way around that is to use an approach as below:

var result = Map
   .Select(t => (t, IsResult:true))
   .FirstOrDefault(w => w.t.Item1 == 4 && w.t.Item2 == 4);

Console.WriteLine(result.IsResult ? "Found" : "Not found");

This sample uses C# 7.1 implied tuple names (and ValueTuple package for C# 7), but you can give the name to your tuple elements explicitly if required, or use a simple Tuple<T1,T2> instead.

Related