Aby zrozumieć w pełni czego się spodziewać po FirstOrDefault (lub SingleOrDefault) musisz zrozumieć, czym wyrażenie wartości domyślnych (default) jest.
Default w typach wartościowych
Typy wartościowe w C#: int, long, double, float, byte, char, bool, enum i struktury.
Poniżej kilka przykładów wywołania wyrażenia default. Dla zmiennej typu bool oraz enum jest to również 0, przy czym w przypadku:
- bool,
0odpowiada wartości False - enum,
0odpowiada pierwszej wartości tego enuma (domyślny default) lub tej z przypisaną wartością0 - char,
0, a dokładniej'\0' (U+0000), „null character”
Default w typach referencyjnych
Typy referencyjne w C#: klasy, delegaty, interfejsy, tablice, zmienne string, czyli elementy rozszerzające klasę System.Object oraz System.String
Poniżej kila przykładów wraz z rezultatami otrzymanymi w konsoli.
Default i typ nullable
Zawsze zwracana jest wartość null. Dla pełnej pewności sprawdzenie poniżej przez is null. Więcej o typach nullable i jak możesz do nich podejść, przeczytasz w tym artykule.
FirstOrDefault() vs SingleOrDefault()
Różnica między First a Single
Obydwie metody (First() oraz Single()) zwracają pojedynczy element z kolekcji. Z tą różnicą, że Single daje zapewnienie istnienia jednego elementu spełniającego warunek, a First zwraca pierwszy element spełniający warunek. W przypadku braku uporządkowania elementów (OrderBy()) zwracane elementy w kolejnych wywołaniach tego samego kodu mogą okazać się losowe.
Or Default – Nie Daj Się Zaskoczyć
Wybierze Pierwszy/Pojedynczy element lub element domyślny.
Przyjrzymy się poniżej pięciu przypadkom i temu co w rezultacie możesz otrzymać.
- Istnieje element spełniający warunek
m.Number == 1– rezultat nie ma wartości null - Nie istnieje element spełniający warunek
m.Number == 1= rezultat ma wartość null - Nie istnieje element spełniający warunek
m.Number == 1, a dodatkowo wybieramy (Select) konkretne pole typu wartościowego.- Rezultat może okazać się niejednoznaczny, wątpliwości rozwiewam w punkcie 5 poniżej.
- Nie istnieje element spełniający warunek
m.Number == 1, a dodatkowo wybieramy (Select) konkretne pole typu nullable
5. Powyższy przykład nr 3, wprowadza niepewność, czy dany element nie został znaleziony w kolekcji, czy został i przyjmuje akurat wartość 0 (domyślną dla long). Zakładając że cały obiekt nie jest potrzebny, możesz dokonać boxingu (new {m.Number}) tego jednego elementu i mieć pewność z jakim przypadkiem masz do czynienia.
Szczególnie przydatne przy optymalizacji operacji na IQueryable, gdy chcemy pobierać z bazy tylko te dane które są potrzebne.
Ciekawostka
Operator default możesz wykorzystać jak na poniższym przykładzie, aby utworzyć wartość domyślną typu:
int a = default(int);Od wersji języka C# 7.1, można użyć samego słowa kluczowego default aby zainicjować zmienną z domyślną wartością:
int a = default;Default a Swich
Warto dodać, że default jest też wykorzystywany w instrukcji wyboru switch. Przy czym w tej instrukcji definiuje domyślne działanie, a nie reprezentuje wartości, jak np. default(int).
Podsumowanie
Znajomość wartości domyślnych i ich zachowań z pewnością przyda się w programowaniu. Nie daj się zaskoczyć metodom FirstOrDefault/SingleOrDefault i zredukuj czas poświęcony debugowaniu i szukania przyczyn buga / problemu / niechcianego zachowania kodu.
Po więcej ciekawych treści zapraszam do do listy mailowej oraz do strony Programista Jutra na FB.

