Metody rozszerzające to dodatek, który pojawił się w C# w wersji 3.0. Metoda taka umożliwia nam dodanie metod do istniejących typów bez potrzeby tworzenia nowego typu pochodnego, rekompilacji lub modyfikacji oryginalnego typu. Można powiedzieć, że metody te rozszerzają funkcjonalność istniejących typów w .NET. Metoda taka jest metodą statyczną, która istnieje w klasie statycznej. Metodę rozszerzającą wywołujemy w ten sam sposób co inne metody.


Funkcje oraz właściwości metod rozszerzających

Poniżej lista podstawowych funkcji oraz właściwości metod rozszerzających:

  • jest to metoda statyczna;
  • musi być umieszczona w klasie statycznej;
  • wykorzystuje słowo kluczowe this jako pierwszy parametr z typem zdefiniowanym w środowisku .NET;
  • metoda ta jest pokazywana przez Intellisense. Kiedy wciśniemy przycisk kropki (.) po instancji typu, metoda ta zostanie wyświetlona;
  • metoda rozszerzająca powinna być w tej samej przestrzeni nazw ponieważ lub aby jej użyć należy zaimportować przestrzeń nazw używając słowa kluczowego using;
  • możesz podać dowolną nazwę klasy, która ma metodę rozszerzającą ale klasa ta musi być statyczna;
  • jeżeli chcesz dodać nowe metody dla danego typu i nie posiadasz kodu źródłowego wtedy rozwiązaniem jest przygotowanie metod rozszerzających dla tego typu;
  • jeśli tworzysz metody rozszerzające, które mają taką samą sygnaturę jak istniejące metody dla danego typu, wówczas metoda rozszerzająca nigdy nie zostanie wywołana.


Tworzenie metod rozszerzających

Utworzymy metodę rozszerzającą dla typu string więc string będzie określony jako parametr tej metody rozszerzającej i będzie wywoływany przez instancję typu string oraz operator kropki (.).
Poniżej przykład definicji metody rozszerzającej:

public static int WordCounter(this string str)
{
    // pobrany łańcuch znaków dzielimy na pojedyczne wyrazy. Są one dzielone po znakach spacji,
    // kropki oraz znaku zapytania. Jeżeli pojawią się puste ciągi znaków będą ignorowane
    string[] userText = str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries);
    // sprawdzamy liczbę wystąpień naszych słów
    int counter = userText.Length;
    return counter;
}

W powyższym przykładzie metody WordCounter() jako parametr przekazujemy typ string więc metoda rozszerzającą będzie wywoływana na danych typu string.

Dla przykładu utworzymy klasę statyczną, która będzie miała w sobie dwie statyczne metody. Jednia z nich będzie zliczała wyrazy a druga policzy wszystkie znaki z pominięciem białych znaków:

namespace ExtensionMethod
{
    static class ExtensionMethodClass
    {
        public static int WordCounter(this string str)
        {
            // pobrany łańcuch znaków dzielimy na pojedyczne wyrazy. Są one dzielone po znakach spacji,
            // kropki oraz znaku zapytania. Jeżeli pojawią się puste ciągi znaków będą ignorowane
            string[] userText = str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries);
            // sprawdzamy liczbę wystąpień naszych słów
            int counter = userText.Length;
            return counter;
        }
        public static int TotalCharactersWithoutSpace(this string str)
        {
            int counter = 0;
            // w tej metodzie pobrany łańcuch znaków dzielimy na wyrazy jedynie po znaku spacji
            // Dlaczego tak? Chcemy policzyć wszystkie znaki, włącznie z ',' czy '.'
            string[] userText = str.Split(' ');
            // pobieramy każdy z wyrazów a następnie sprawadzamy ilość znaków
            foreach (var item in userText)
            {
                counter += item.Length;
            }
            return counter;
        }
    }
}

W tej samej przestrzeni nazw dodałem do projektu nową klasę, która została zdefiniowana jako statyczna. Wewnątrz tej klasy znajdują się dwie metody rozszerzające.

Teraz przygotujemy wykonanie naszego programu, który na wejściu przyjmuje łańcuch znaków a następnie wykorzystując metody rozszerzające pozwoli nam policzyć liczbę wyrazów oraz liczbę znaków pomijając znaki spacji:

using System;
namespace ExtensionMethod
{
    class Program
    {
        static void Main(string[] args)
        {
            string text = string.Empty;
            int totalWords = 0;
            int totalCharactersWithoutSpace = 0;
            // Jeżeli chcesz sam podawać swoje zdania, odkomentuj poniższy kod
            // W celach testowych oraz takiego samego wyniku dla każdego zdanie
            // zostane przypisane na sztywno do naszej zmiennej tekstowej
            // Console.WriteLine("Podaj swoje zdanie: ");
            // text = Console.ReadLine();
            text = "Witaj Drogi Uzytkowniku!. Mam nadzieję, że powyższy tutorial jest dla Ciebie zrozumiały.";
            // Teraz użyjemy metod rozszerzających na naszym typie teksowym (string)
            // Używając zapisu: text. Intellisens pokaże nam na liście nasze metody rozszerzające
            // Metody takie oznaczone są obrazkiem pudełka(metoda) ze strzałką w dół (metody rozszerzające)
            // Metody wywołujemy na danym typie, jest on przekazywany jako parametr do naszej metody rozszerzającej
            totalWords = text.WordCounter();
            totalCharactersWithoutSpace = text.TotalCharactersWithoutSpace();
            Console.WriteLine("Liczba wyrazów: {0}", totalWords);
            Console.WriteLine("Liczba znaków: {0}", totalCharactersWithoutSpace);
            Console.ReadKey();
            // Wynik działania programu
            // Liczba wyrazów: 12
            // Liczba znaków: 77
       }
    }
}

A co jeśli chciałbym przekazać więcej parametrów? Czy jest to możliwe, jak tego dokonać?
Oczywiście, że możesz. Poniżej przykład definicji takich metod wraz z przykładem ich użycia (powyższy program został rozwinięty):

using System;
namespace ExtensionMethod
{
    static class ExtensionMethodClass
    {
        public static int WordCounter(this string str)
        {
            // pobrany łańcuch znaków dzielimy na pojedyczne wyrazy. Są one dzielone po znakach spacji,
            // kropki oraz znaku zapytania. Jeżeli pojawią się puste ciągi znaków będą ignorowane
            string[] userText = str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries);
            // sprawdzamy liczbę wystąpień naszych słów
            int counter = userText.Length;
            return counter;
        }
        public static int TotalCharactersWithoutSpace(this string str)
        {
            int counter = 0;
            // w tej metodzie pobrany łańcuch znaków dzielimy na wyrazy jedynie po znaku spacji
            // Dlaczego tak? Chcemy policzyć wszystkie znaki, włącznie z ',' czy '.'
            string[] userText = str.Split(' ');
            // pobieramy każdy z wyrazów a następnie sprawadzamy ilość znaków
            foreach (var item in userText)
            {
                counter += item.Length;
            }
            return counter;
        }
        public static int TwoSentences(this string str, string str2)
        {
            // łączymy dwa ciągi znaków w jeden
            string completeSentence = String.Concat(str, str2);
            // pobrany łańcuch znaków dzielimy na pojedyczne wyrazy. Są one dzielone po znakach spacji,
            // kropki oraz znaku zapytania. Jeżeli pojawią się puste ciągi znaków będą ignorowane
            string[] text = completeSentence.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries);
            // sprawdzamy liczbę wystąpień naszych słów
            int counter = text.Length;
            return counter;
        }
        // Może niezbyt udany przykład ale pokazuje użycie większej liczby parametrów różnych typów
        public static string ConcatParameters(this string str, string str2, bool flaga, int number)
        {
            string myOwnText = string.Empty;
            myOwnText = String.Concat(str, str2, "Przekazana flaga: " + flaga, "\nPrzekazana liczba: " + number);
            return myOwnText;
        }
    }
}

Oraz wykonanie naszego programu:

using System;
namespace ExtensionMethod
{
    class Program
    {
        static void Main(string[] args)
        {
            string text = string.Empty;
            int totalWords = 0;
            int totalCharactersWithoutSpace = 0;
            // Jeżeli chcesz sam podawać swoje zdania, odkomentuj poniższy kod
            // W celach testowych oraz takiego samego wyniku dla każdego zdanie
            // zostane przypisane na sztywno do naszej zmiennej tekstowej
            // Console.WriteLine("Podaj swoje zdanie: ");
            // text = Console.ReadLine();
            text = "Witaj Drogi Uzytkowniku!. Mam nadzieję, że powyższy tutorial jest dla Ciebie zrozumiały.";
            // Teraz użyjemy metod rozszerzających na naszym typie teksowym (string)
            // Używając zapisu: text. Intellisens pokaże nam na liście nasze metody rozszerzające
            // Metody takie oznaczone są obrazkiem pudełka(metoda) ze strzałką w dół (metody rozszerzające)
            // Metody wywołujemy na danym typie, jest on przekazywany jako parametr do naszej metody rozszerzającej
            totalWords = text.WordCounter();
            totalCharactersWithoutSpace = text.TotalCharactersWithoutSpace();
            Console.WriteLine("Liczba wyrazów: {0}", totalWords);
            Console.WriteLine("Liczba znaków: {0}", totalCharactersWithoutSpace);
            // Dodajemy nowe wywołania metod rozszerzających do naszego programu
            totalWords = text.TwoSentences("A w tym miejscu dopisujemy nasze drugie zdanie/przekazujemy parametr typu string");
            Console.WriteLine("Liczba wyrazów po połączeniu: {0}", totalWords);
            // Możemy przekazywać różną ilość parametrów, różnych typów
            string myOwnText = text.ConcatParameters("Zapraszam do innych poradników.", true, 8);
            Console.WriteLine("Formatowanie: {0}", myOwnText);
            Console.ReadKey();
            // Wynik działania programu
            // Liczba wyrazów: 12
            // Liczba znaków: 77
            // Liczba wyrazów: 12
            // Liczba znaków: 77
            // Liczba wyrazów po polaczeniu: 23
            // Formatowanie: Witaj Drogi Uzytkowniku!. Mam nadzieje, ze powyzszy tutorial jest
            // dla Ciebie zrozumialy.Zapraszam do innych poradników.Przekazana flaga: True
            // Przekazana liczba: 8
       }
    }
}