Bezpieczny null-check w C#

Dziś krótki wpis, który być może okaże się dla Ciebie bardzo przydatny. Chodzi o jedną z technik programowania defensywnego, którą bardzo często możesz spotkać w kodzie C# (z uwagi na jego specyfikę), a mianowicie null-check (ogólniej asercja). Spójrz na poniższy kod

 


    class Program
    {
        static void Main(string[] args)
        {
            Program p = null;

            if(p == null)
            {
                Console.WriteLine("p is null");
            }
            else
            {
                Console.WriteLine("p is not null");
            }
            Console.ReadLine();
        }
    }

 

Wynik działania programu:

 

 

Jest to bardzo typowe sprawdzenie czy zmienna typu referencyjnego jest null-em, czy też nie. Wszyscy widzimy to często zaraz po pobraniu danych z DAL (ang. data access layer), pojawia się to także jako „walidacja” danych w kontrolerze ASP.NET itd. Jest to fundament programowania w C#, który każdy doskonale zna i rozumie. Ma on tylko jedną wadę… nie zawsze działa.  Zmodyfikujmy nieco powyższy kod:

 


    class Program
    {
        static void Main(string[] args)
        {
            Program p = null;

            if(p == null)
            {
                Console.WriteLine("p is null");
            }
            else
            {
                Console.WriteLine("p is not null");
            }
            Console.ReadLine();
        }

        public static bool operator ==(Program a, Program b)
            => false;

        public static bool operator !=(Program a, Program b)
            => true;
    }

 

W programie dodałem przeciążenie dla operatora == oraz != (ten drugi wymusił sam C#, ponieważ operatory porównania muszą być przeciążane parami). Jak widzisz implementacja nakazuje, aby operator == zawsze zwracał wartość false. W związku z powyższym wynik działania programu nie powinien być dla Ciebie zaskoczeniem:

 

 

Mimo tego iż zmienna  była null-em, wyrażenie w bloku if (poprawnie) przeprowadziło ewaluację do wartości false, tym samym wypisując zakłamany dla użytkownika komunikat.

W jaki sposób ustrzec się zatem przed ewentualnym przeciążaniem operatora równości? Stosując słowo kluczowe is. Zmodyfikujmy powyższy kod raz jeszcze, wprowadzając odpowiednią korektę:

 


    class Program
    {
        static void Main(string[] args)
        {
            Program p = null;

            if(p is null)
            {
                Console.WriteLine("p is null");
            }
            else
            {
                Console.WriteLine("p is not null");
            }
            Console.ReadLine();
        }

        public static bool operator ==(Program a, Program b)
            => false;

        public static bool operator !=(Program a, Program b)
            => true;
    }

 

Tym razem wynik (mimo przeciążenia operatorów) jest już taki, jakiego się spodziewaliśmy:

 

 

Ten ficzer jest stosunkowo nowy w języku, ponieważ został dodany w wersji 7.0 przy okazji pattern matching-u. Jest to tzw. constant pattern. Mam jednak wrażenie, że mimo wszystko wiele osób najzwyczajniej w świecie go przegapiła, a szkoda bo może nie raz uratować nam skórę… i uchronić przed długimi sesjami debugowania.

You may also like...

  • Pingback: dotnetomaniak.pl()

  • Z innej zaś strony, jak często przeciążamy operator równości? Stosunkowo rzadko;-)
    Niemniej jednak, jest to wiedza którą warto posiadać, przecież diabeł tkwi w szczegółach 😀