Jeżeli czytałeś posty lub artykuły na MSDN poświecone nowym funkcjonalnością języka C# 6.0 napewno natrafiłeś na informację, że operator warunkowy null może Ci pomóc znacznie zmniejszyć liczbę trudnych do zdebugowania oraz zreprodukowania błędów typu: NullReferenceException.
Po pierwszym przeczytaniu wspomnianego wyżej posta udało mi się jedynie zarejestrować, że operator ten pomaga w łańcuchowym sprawdzaniu wartości null zwłaszcza w zagnieżdzonych strukturach danych oraz, że zwraca wartość null jak tylko znajdzie takie wystąpienie w całym łańcuchu danych. Myślałem, że jest to jedynie uproszczenie składniowe.
Nie zrozumiałem jednak prawdziwej mocy tego operatora. Zwykle preferuje drobiazgowe sprawdzenia, które przy użyciu C# 5.0 mogłby być przedstawione w poniższy sposób:
private static int GetCurrentCarSpped(Car car) { if(car!=null && car.Engine!=null && car.Engine.ControlUnit!=null) { return car.Engine.ControlUnit.CurrentCarSpeed; } return 0; }
W celu łatwiejszej wizualizacji struktury danych poniżej przygotowane klasy:
class Car { public Engine Engine { get; set; } } class Engine { public ControlUnit ControlUnit { get; set; } } class ControlUnit { public int CurrentCarSpeed { get; set; } }
Pojawa się pytanie. Czy takie sprawdzenie jest koniecznie, czy musimy przechodzić przez każdy element z osobna? Czy możemy zastosować tutaj operator warunkowy null? Spójrzmy na poniższą konstrukcję:
private static int GetCurrentCarSpeed(Car car) { return car?.Engine?.ControlUnit?.CurrentCarSpeed ?? 0; }
Pozbyliśmy się 5 linijek kodu, było warto?
Było warto ponieważ taka konstrukcja jest bezpieczna dla wątku – kompilator generuje kodu do oceny właściwości tylko jeden raz przechowując ten wynik w zmiennej tymczasowej.
Mam nadzieje, że po takiej definicji wszystko stanie się jasne. Żeby jednak prześledzić cały ten proces dogłębienie skupimy się na analizie kodu IL (Intermediate Language). Cały kod przykładu znajduje się w artykule dlatego możemy przystąpić do utworzenia dwóch aplikacji konsolowych w celu porówniania rezultatu. Następnie użyjemy narzędzia JetBrains dotPeek do zbadania kodu IL, który został utworzony przez każdy z kompilatorów.