Extension Methods

Dzięki temu mechanizmowi można tak jakby dodać metodę do klasy bez modyfikacji tej klasy (np. do już skompilowanej dll’ki).

Implementacja krok po kroku

Przykładowa implementacja minimum:

    namespace KMorcinek.Common
    {
        public static class XmlExtensions
        {
            public static void Foo(this XmlNode parent, string nodeName)
            {
                // ...
            }
        }
    }

Klasa jak i metoda muszą być statyczne (static). Nazwę najlepiej złożyć z dwóch członów – NazwaKlasyExtensions. Na przykład dla StringExtensions grupujemy wszystkie extension methods rozszerzające klasę string.

Modyfikatory dostępu public dla klasy i metody nie są konieczne (mogłyby być internal), jednak chyba po to tworzymy takie klasy, żeby były reużywalne w wielu projektach, więc public się przydaje.

this

Aby mechanizm Extension Method zadziałał, pierwszy argument musi być poprzedzony przez „this„. Dzięki temu słowu kluczowemu dzieje się cała magia. Na instancji o typie takim jak pierwszy argument będziemy wywoływać metodę (rozszerzymy klasę o metodę). Niech to słowo kluczowe za bardzo was nie zmyli, nie mamy dostępu do prywatnych czy protected pól i metod.

Wywołanie

Przedstawiona metoda może być wywołana na dwa sposoby:

using KMorcinek.Common
// ...
    void Bar(XmlNode node)
    {
        // Wywołanie statycznej metody klasy XmlExtensions
        XmlExtensions.Foo(node,"");

        // Wywołanie Extension method na instancji klasy XmlNode
        node.Foo("");
    }

Extension Methods wprowadzono w .NET Framework 3.5. Wywołanie pierwsze było dostępne wcześniej (nie mogło być wtedy słówka „this” w deklaracji metody). Wywołanie drugie to właśnie tytułowa Extension Method. Pozwala ona na ładniejsze, skrócone wywołania. W obydwu przypadkach jest generowany dokładnie taki sam kod pośredni (sprawdzone empirycznie :)). Całą magię wykonuje kompilator C#. Pozwala on zapisać skrócone node.Foo(„”); jednak wygneruje kod, który wywoła metodę statyczną jak w pierwszym przypadku.

Aby IntelliSense podpowiadało nam metodę Foo(), musimy zaimportować przestrzeń nazw, w której ta metoda (klasa) się znajduje.

Zastosowanie w LINQ

Główne zastosowanie to LINQ, to ono było chyba powodem, dla którego wprowadzono ten mechanizm. Wyobraźcie sobie np. wywołanie:

    var result = CollectionExtensions.Distinct(CollectionExtensions.StartsWith(items, n => n.StartsWith("A")));

zamiast znanego LINQowego :

    var result = items.Select(n => n.StartsWith("A")).Distinct();

Jaki namespace wybrać?

Zazwyczaj umieszczam je w katalogu (przestrzenie nazw powinny odzwierciedlać fizyczne katalogi) Common. Złym IMHO zwyczajem jest wybieranie przestrzeni nazw klasy którą rozszerzamy.

Powiedzmy, że umieściliśmy metodę GetFirstLetter() dla stringa tam gdzie string czyli w System. Wtedy w całym projekcie na każdym stringu będzie można wywołać metodę GetFirstLetter() i zawsze będzie ona podpowiadana. Spowoduje to bałagan.

Ja jestem pełen szacunku dla twórców frameworka i gdyby jakaś metoda jeszcze na stringu była potrzebna zawsze i wszędzie, to by ją już dawno dodali 🙂

Reklamy
Ten wpis został opublikowany w kategorii Programowanie i oznaczony tagami , . Dodaj zakładkę do bezpośredniego odnośnika.

6 odpowiedzi na „Extension Methods

  1. Pingback: dotnetomaniak.pl

  2. coola pisze:

    Co to jest frist 🙂 ?

  3. Dzięki, poprawione. Prościej mi wyłapać polskie literówki 😉

  4. Arkadiusz Bal pisze:

    A ja wolę stosować oba rozwiązania dt. namespaców, oznaczam folder Małpką np. tak @System
    i w środku mam StringExtensions i w środku rzeczywiście trzymam metody przydatne w całej aplikacji. Jeżeli dotyczą konkretnego problemu np. Grafów, a pracują na uniwersalnych typach(np. IEnumerable, Collection, czy object) to wtedy oddzielny namespace ma zastosowanie.
    Ale to oczywiście moje zdanie.

    • Ciekawe, nie znałem tego. Mógłbyś wyjaśnić co robi ta Małpka (może jakiś artykuł)? Jest to chyba jakiś feature .NETowy, bo importując taki namespace MyCompany.@System mogę zapisać zarówno z małką jak i bez niej.

  5. Arkadiusz Bal pisze:

    Nie nie korzystam z tego jako feature. 🙂
    Małpka w c sharp to akurat

    To jedynie info dla mnie i innych devów w projekcie, że w środku tego folderu odnoszę się do innego namespacu, tj. np w folderze ZI.Controls/Prism/Extensions/@Microsoft.Practices.Prism/Regions/RegionAdapterExtensions. Trzymam klasę Microsoft.Practices.Prism.Regions.RegionAdapterExtensions.

Możliwość komentowania jest wyłączona.