C# Language DefaultIfEmpty


Exemple

DefaultIfEmpty est utilisé pour renvoyer un élément par défaut si la séquence ne contient aucun élément. Cet élément peut être la valeur par défaut du type ou une instance définie par l'utilisateur de ce type. Exemple:

var chars = new List<string>() { "a", "b", "c", "d" };

chars.DefaultIfEmpty("N/A").FirstOrDefault(); // returns "a";

chars.Where(str => str.Length > 1)
     .DefaultIfEmpty("N/A").FirstOrDefault(); // return "N/A"

chars.Where(str => str.Length > 1)
        .DefaultIfEmpty().First(); // returns null;

Utilisation dans les jointures à gauche :

Avec DefaultIfEmpty la jointure Linq traditionnelle peut renvoyer un objet par défaut si aucune correspondance n'a été trouvée. Agissant ainsi comme une jointure de gauche de SQL. Exemple:

var leftSequence = new List<int>() { 99, 100, 5, 20, 102, 105 };
var rightSequence = new List<char>() { 'a', 'b', 'c', 'i', 'd' };

var numbersAsChars = from l in leftSequence
                     join r in rightSequence
                     on l equals (int)r into leftJoin
                     from result in leftJoin.DefaultIfEmpty('?')
                     select new
                     {
                         Number = l,
                         Character = result
                     };

foreach(var item in numbersAsChars)
{
    Console.WriteLine("Num = {0} ** Char = {1}", item.Number, item.Character);
}

ouput: 

Num = 99         Char = c
Num = 100        Char = d
Num = 5          Char = ?
Num = 20         Char = ?
Num = 102        Char = ?
Num = 105        Char = i

Dans le cas où un DefaultIfEmpty est utilisé (sans spécifier de valeur par défaut) et qu'il en résultera aucun élément correspondant sur la bonne séquence, il faut s'assurer que l'objet n'est pas null avant d'accéder à ses propriétés. Sinon, cela entraînera une NullReferenceException . Exemple:

var leftSequence = new List<int> { 1, 2, 5 };
var rightSequence = new List<dynamic>()
    {
        new { Value = 1 },
        new { Value = 2 },
        new { Value = 3 },
        new { Value = 4 },
    };

var numbersAsChars = (from l in leftSequence
                        join r in rightSequence
                        on l equals r.Value into leftJoin
                        from result in leftJoin.DefaultIfEmpty()
                        select new
                        {
                            Left = l,
                            // 5 will not have a matching object in the right so result 
                            // will be equal to null. 
                            // To avoid an error use:
                            //    -  C# 6.0 or above - ?. 
                            //    -  Under           - result == null ? 0 : result.Value
                            Right = result?.Value
                        }).ToList();