Metoda rozszerzająca nie boi się nulla

Metody rozszerzające pojawiły się w C# 3.0. Od tamtej pory towarzyszą nam na każdym kroku, sam kiedyś nie zdawałem sobie sprawy z ich używania 😉

Metoda rozszerzająca, czyli taka, która rozszerza, powiększa zestaw metod jakimi dysponuje jakaś klasa/struktura, nawet w dawno skompilowanej DLLce. Ogólnie ten temat poruszany był w internetach wielokrotnie, ja chciałem jednak zaznaczyć bardzo ciekawą właściwość metod rozszerzających, o której często się zapomina.

Metoda rozszerzająca nie boi się nulla!

Jak to?

Wywołanie metody rozszerzającej wygląda w kodzie dokładnie tak samo jak wywołanie metody „normalnej”, czyli np

string x = "alaMaKota";
x.ToString(); //metoda "normalna";
x.ExtensionMethod(); //metoda rozszerzająca, o ile taką mamy gdzieś zadeklarowaną

dobra, przypuśćmy, że mamy taki kodzik:

static class ExtensionMethods
{
   public static int GetLengthExt(this string value)
   {
        if (!string.IsNullOrWhiteSpace(value))
        {
           return value.Length;
        }
        return -1;
    }
}

//wywołanie
var strArray = new[] { "10", null, "alaMaKota" };
foreach (var str in strArray)
{
    Console.WriteLine(str.GetLengthExt());
}
//output
/*
2
-1
9
*/

i o dziwo, przy drugim obiegu pętli foreach nie dostaniemy NullReferenceException, co wydarzyłoby się gdyby nie była to metoda rozszerzająca. Gdzie więc ta magia?

Kolejny przykład i pod nim wyjaśnienie:

using System;

namespace TestExt
{
    class Program
    {
        static void Main(string[] args)
        {
            string x = null;
            Console.WriteLine(x.GetLengthExt());
            Console.WriteLine(GetLengthStatic(x));
        }

        static int GetLengthStatic(string str)
        {
            if (!string.IsNullOrWhiteSpace(str))
            {
                return str.Length;
            }
            return -1;
        }
    }

    static class ExtMethods
    {
        public static int GetLengthExt(this string str)
        {
            if (!string.IsNullOrWhiteSpace(str))
            {
                return str.Length;
            }
            return -1;
        }
    }
}

otóż nie ma praktycznie żadnej różnicy w IL dla wywołania:

//x.GetLengthExt();
IL_0004: call int32 TestExt.ExtMethods::GetLengthExt(string)
IL_0009: call void [mscorlib]System.Console::WriteLine(int32)

//StaticGetLength(x);
IL_0010: call int32 TestExt.Program::GetLengthStatic(string)
IL_0015: call void [mscorlib]System.Console::WriteLine(int32)

także wywołanie metody rozszerzającej jest zamieniane na wywołanie „zwykłej metody statycznej” 🙂 Stąd też „odporność” na nulle. Ładnie.

Oczywiście odporność na nulle jest też oczywiście uzależniona od implementacji, jednakże jeżeli wewnątrz metody nie zapomnimy sprawdzić czy obiekt wywołujący nie jest nullem, nic złego się nie stanie.

Taka to była ciekawostka.