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,
0
odpowiada wartości False - enum,
0
odpowiada 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.