Blog Dla Młodszych Programistów C#/.NET

wtorek, 29 września 2020
Pisząc aplikacje, często będziesz korzystał z gotowych już bibliotek, klas, które zostały napisane przez inne osoby i nie będziesz miał dostępu do ich kodu źródłowego. Jeżeli będziesz potrzebował jakiejś metody, której akurat nie ma w danej klasie, to zapewne nie będziesz szukał ich autora, tylko sam taką metodę spróbujesz dopisać. Zastanawiasz się pewnie, w jaki sposób możesz to zrobić. Na pewno pierwsze co pomyślałeś, skoro trzeba rozszerzyć klasę, to trzeba skorzystać z dziedziczenia lub kompozycji. Także w przypadku dziedziczenia musiałbyś dodać nową klasę, która będzie dziedziczyć po klasie, którą chcesz rozszerzyć i w niej możesz dodać nową metodę i napisać jej implementację. Oczywiście ten sposób nie jest idealny, ponieważ będziesz musiał tworzyć mnóstwo nowych klas oraz będziesz musiał zmieniać w Twoim kodzie wszystkie wywołania. Możesz też, zamiast używać dziedziczenia i kompozycji napisać nową metodę, która będzie daną klasę przyjmowała jako parametr i na podstawie jej wartości wykonywać jakieś działanie i następnie zwróci odpowiednia wartość. Czy jest może lepsze rozwiązanie? Tak, tutaj właśnie z pomocą przychodzą metody rozszerzające (extension methods). O tym, czym są metody rozszerzające i jak ich używać w swoim kodzie, dowiesz się z tego artykułu.

Metody Rozszerzające w C#, Czyli Jak w Łatwy Sposób Rozszerzyć Już Istniejący Typ


Problem, który chcemy rozwiązać


Załóżmy, że mamy do rozwiązania prosty problem. Wyświetlamy tabelę z produktami, między innymi z polem typu string - opis. Ten opis powiedzmy, że jest przechowywany w bazie danych i może mieć maksymalną wielkość 300 znaków. Jednak my w tabeli chcemy wyświetlić tylko 20 pierwszych znaków. Jeżeli dany opis ma 20 znaków lub mniej, to wtedy wyświetlamy cały opis, jednak jeżeli opis produktu ma więcej niż 17 znaków, to wtedy wyświetlamy 17 pierwszych znaków i dodajemy przyrostek "...". Dzięki temu opis może mieć maksymalnie 20 znaków. Fajnie, jeżeli to rozwiązanie będzie uniwersalne i będzie można go użyć w wielu miejscach, w różnych klasach i projektach. Jak można zaimplementować takie rozwiązanie?


Rozwiązanie 1 - Dodanie zwykłej metody statycznej do projektu


Wydaje mi się, że pierwsze najbardziej intuicyjne rozwiązanie, to dodanie nowej metody statycznej, która jako parametr będzie przyjmować string'a, czyli opis, który następnie zmodyfikuje, jeżeli będzie taka potrzeba i zwróci pierwszych 20 znaków. Tak może wyglądać przykładowa implementacja wraz z wywołaniem:
namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            var description = "12345678901234567890X";
            var shortDescription = StringMethods.GetTheFirstNCharacters(description, 20);
            Console.WriteLine(shortDescription);
            ///12345678901234567...
        }
    }

    public static class StringMethods
    {
        public static string GetTheFirstNCharacters(string val, int maxNumberOfCharacters)
        {
            if (string.IsNullOrWhiteSpace(val))
                return string.Empty;

            if (val.Length <= maxNumberOfCharacters)
                return val;

            return val.Substring(0, maxNumberOfCharacters - 3) + "...";
        }
    }
}

Jak widzisz, rozwiązanie to spełnia wszystkie założenia, ale czy jest ono idealne? Nie, myślę, że można to rozwiązanie napisać lepiej, dzięki metodom rozszerzającym. Jak w takim razie, powinno wyglądać to rozwiązanie?


Rozwiązanie 2 - Wykorzystanie metody rozszerzającej


Drugie rozwiązanie już może nie być dla wszystkich takie intuicyjne, ale myślę, że w tym przypadku będzie dużo lepsze. To znaczy, możemy skorzystać właśnie z tytułowych metod rozszerzających, dzięki czemu rozszerzymy klasę string o nową metodę, którą będziemy mogli używać w naszych aplikacjach. Także dzięki metodom rozszerzającym, możemy rozszerzyć dowolną klasę bez używania dziedziczenia.
namespace App
{
    class Program
    {
        static void Main(string[] args)
        {
            var description = "12345678901234567890X";
            var shortDescription = description.GetTheFirstNCharacters(20);
            Console.WriteLine(shortDescription);
            //12345678901234567...
        }
    }

    public static class StringExtensions
    {
        public static string GetTheFirstNCharacters(this string val, int maxNumberOfCharacters)
        {
            if (string.IsNullOrWhiteSpace(val))
                return string.Empty;

            if (val.Length <= maxNumberOfCharacters)
                return val;

            return val.Substring(0, maxNumberOfCharacters - 3) + "...";
        }
    }
}

Jeżeli chodzi o logikę, to tutaj nie ma żadnych zmian, ale w tym przypadku użyłem metody rozszerzającej, dzięki czemu klasa string została rozszerzona o nową metodę. Możesz używać metody GetTheFirstNCharacters jak zwykłej metody klasy string. Jest ta metoda również podpowiadana przez intellisense po wpisaniu kropki po nazwie zmiennej. Wydaje mi się, że taki zapis jest dużo bardziej czytelny i uniwersalny. Nie musisz tutaj wywoływać żadnych statycznych metod z innych klas, tylko wywołujesz metodę rozszerzającą jak każdą inną metodę klasy string. Ponadto możesz takie metody rozszerzające wyodrębnić do osobnego projektu i używać ich w innych swoich aplikacjach, jeżeli będzie taka potrzeba.


Jak pisać metody rozszerzające?


Aby metodą była metodą rozszerzającą, to musi spełniać kilka warunków:
  • Musi zostać zaimplementowana w klasie statycznej
  • Metoda rozszerzająca musi być statyczna
  • Pierwszy parametr metody rozszerzającej musi zostać poprzedzony słowem kluczowym this, przed nazwą klasy, którą rozszerza
Co ważne, zwróć uwagę, że do metody rozszerzającej nie przekazujemy pierwszego argumentu, który jest poprzedzony słowem this, ponieważ to jest właśnie typ, który rozszerzamy. Także naszą metodę wywołuje na tym typie. Ta wartość jest przekazywana automatycznie.

Jeżeli intellisense nie będzie podpowiadał składni, to znaczy nie będzie pokazywał Twojej metody, to prawdopodobnie (jeżeli dobrze zdefiniowałeś metodę rozszerzającą) metoda została napisana w innym namespace'ie, dlatego również musisz dodać odpowiedniego using'a w tym pliku.

Ja w swoich projektach często korzystam z takich metod rozszerzających. Zawsze trzymam się takiej konwencji, że dana metoda rozszerzająca znajduje się w klasie o nazwie: rozszerzany typ + przyrostek Extensions. To znaczy dla string'a, będzie to klasa o nazwie StringExtensions tak jak w powyższym przykładzie. Takie metody oraz odpowiednia struktura projektu zwiększa czytelność kodu.


Przykłady metod rozszerzających


W c# często spotykasz się z różnymi metodami rozszerzającymi. Często z nich korzystasz, a możesz nawet o tym nie wiedzieć. Przykładem takich metod są metody LINQ. To znaczy na przykład obliczanie sumy kolekcji IEnumerable:
var myNumbers = new List<int> { 1, 2, 3 };
myNumbers.Sum();

A tak wygląda sygnatura metody Sum:
public static int Sum(this IEnumerable<int> source);

Jak widzisz, jest to metoda rozszerzająca klasę IEnumerable.


PODSUMOWANIE:


Jak widzisz, metody rozszerzające mogą mieć różne zastosowanie, ja w swoich projektach często z nich korzystam, w celu poprawienia czytelności kodu. Dzięki nim możesz w łatwy sposób rozszerzyć przede wszystkim kod, do którego nie masz dostępu. Jeżeli chcesz zostać programistą .NET, to znajomość tego tematu również może Ci się przydać na rozmowach kwalifikacyjnych. Rekruterzy lubią poruszyć temat metod rozszerzających, jeżeli interesują Cię takie zagadnienia, to jakiś czas temu przygotowałem specjalny dokument PDF, w którym znajdziesz pytania i odpowiedzi na 10 najczęstszych pytań z rozmów kwalifikacyjnych (+bonus) na stanowisko młodszego programisty .NET (🔥 tutaj dokument PDF). Jeżeli masz jakieś pytania co do tego tematu, to napisz, proszę komentarz poniżej lub napisz do mnie e-mail'a 🙂

Poprzedni artykuł - Logowanie Danych Do Pliku w C# Za Pomocą Biblioteki NLog.
Następny artykuł - Za Stary Na Programowanie? Czy Można Zostać Programistą Po 30-stce?
Autor artykułu:
Kazimierz Szpin
Kazimierz Szpin
Programista C#/.NET. Specjalizuje się w ASP.NET Core, ASP.NET MVC, ASP.NET Web API, Blazor, WPF oraz Windows Forms.
Autor bloga ModestProgrammer.pl
Komentarze (1)
michael112
MICHAEL112, środa, 1 września 2021 18:58
Poza oczywistymi zaletami, pozwolę sobie wspomnieć o pewnej dość poważnej wadzie metod rozszerzających: jest to piekielnie trudne dla osób próbujących przeanalizować kod. W sytuacji kiedy takie metody nie istnieją, spoglądam sobie na kod klasy (plus jej drzewo dziedziczenia) i już wiem, jakie metody można wywołać na tej klasie. Konia z rzędem temu, kto będzie w stanie przejrzeć wszystkie przestrzenie nazw dołączone do danego projektu aby odnaleźć poszczególne metody rozszerzające.
Dodaj komentarz

Wyszukiwarka

© Copyright 2024 modestprogrammer.pl. Wszelkie prawa zastrzeżone. Regulamin. Polityka prywatności. Design by Kazimierz Szpin