Jakie zalety metod przedłużających znalazłeś? [Zamknięte]


84

„Niewierny” C # zapytał mnie, jaki jest cel metod rozszerzających. Wyjaśniłem, że możesz następnie dodać nowe metody do obiektów, które zostały już zdefiniowane, zwłaszcza jeśli nie jesteś właścicielem / kontrolujesz źródła oryginalnego obiektu.

Przywołał: „Dlaczego po prostu nie dodać metody do własnej klasy?” Chodziliśmy w kółko (w dobry sposób). Moja ogólna odpowiedź jest taka, że ​​jest to kolejne narzędzie w pasku narzędzi, a jego odpowiedź jest taka, że ​​jest to bezużyteczne marnowanie narzędzia ... ale pomyślałem, że otrzymam bardziej „oświeconą” odpowiedź.

Jakie są scenariusze, w których korzystałeś z metod rozszerzających, których nie mógłbyś (lub nie powinien był) użyć metody dodanej do własnej klasy?


2
Myślę, że są na pewno ważne punkty, które ludzie mogą poczynić (i poczynili) na poparcie metod rozszerzających ... ale zdecydowanie nie ma scenariusza, w którym „ nie można by było ” użyć metod statycznych zamiast metod rozszerzających. Rozszerzenie metod naprawdę tylko metody statyczne, do których dostęp w inny sposób. Tylko coś do zapamiętania.
Dan Tao

@DanTao, w lżejszym tonie, jedyną rzeczą, która sprawia, że ​​wywołanie metodą rozszerzenia jest niezastąpione, jest ich nazewnictwo. Extensions.To(1, 10)jest bez znaczenia i 1.To(10)ma charakter opisowy. Oczywiście rozumiem techniczną stronę, o której mówisz, po prostu mówiąc. W rzeczywistości istnieją scenariusze, w których „ nie można by” zastosować podejścia rozszerzającego zamiast metod statycznych, na przykład odbicie, jest inny przypadek dynamic.
nawfal

Odpowiedzi:


35

Myślę, że metody rozszerzające bardzo pomagają w pisaniu kodu, jeśli dodasz metody rozszerzające do podstawowych typów, szybko je uzyskasz w intelisense.

Mam dostawcę formatu do formatowania rozmiaru pliku . Aby z niego skorzystać muszę napisać:

Console.WriteLine(String.Format(new FileSizeFormatProvider(), "{0:fs}", fileSize));

Tworząc metodę rozszerzającą mogę napisać:

Console.WriteLine(fileSize.ToFileSize());

Czystsze i prostsze.


4
bardziej opisowa nazwa metody rozszerzenia sprawiłaby, że byłaby ona jeszcze czystsza. np. fileSize.ToFormattedString ();
David Alpert

1
mmm, ToFormattedFizeSize () może, jest to metoda rozszerzająca dla typu Long, ToFormattedString () nie jest nazwą opisową
Eduardo Campañó

3
wskazano jednak. kiedy czytam fileSize.ToFileSize (), pytam, dlaczego duplikacja? co on właściwie robi? Wiem, że to tylko przykład, ale opisowe nazwy pomagają uczynić go czystym i prostym.
David Alpert

1
masz rację, ponieważ w przypadku testów bardzo ważne jest, aby wybrać odpowiednią nazwę
Eduardo Campañó

5
Nie jest to jednak bezpośrednia odpowiedź na pytanie, ponieważ podany przykład nie musi być metodą rozszerzającą. Mogłeś to zrobić LongToFileSize(fileSize), co jest równie zwięzłe i prawdopodobnie równie jasne.
Dan Tao

83

Tylko zaletą metod rozszerzenie jest czytelność kodu. Otóż ​​to.

Metody rozszerzające pozwalają na to:

foo.bar();

zamiast tego:

Util.bar(foo);

Teraz w C # jest wiele takich rzeczy. Innymi słowy, istnieje wiele funkcji języka C #, które wydają się trywialne i same w sobie nie przynoszą wielkich korzyści. Jednak gdy zaczniesz łączyć te funkcje razem, zaczniesz widzieć coś nieco większego niż suma jego części. LINQ znacznie korzysta z metod rozszerzających, ponieważ zapytania LINQ byłyby prawie nieczytelne bez nich. LINQ byłoby możliwe bez metod rozszerzających, ale nie jest praktyczne.

Metody rozszerzające są bardzo podobne do klas częściowych języka C #. Same w sobie nie są zbyt pomocne i wydają się trywialne. Ale kiedy zaczynasz pracę z klasą, która potrzebuje wygenerowanego kodu, klasy częściowe zaczynają mieć dużo więcej sensu.


1
Staje się to jeszcze ważniejsze, jeśli używasz łańcuchowych metod, takich jak x.Foo (coś) .Bar (coś innego) .Baz (jeszcze coś). Jest to najbardziej rozpowszechnione w metodach rozszerzających IEnumerable <> i znacznie zwiększa czytelność
ShuggyCoUk

2
Nie rozumiem, że foo.bar () sugeruje mi, że bar () jest metodą foo. Więc kiedy to nie działa, szukam w złym miejscu. Nie jestem pewien, czy jest to dla mnie poprawa czytelności.
Martin Brown

3
Kliknięcie prawym przyciskiem myszy na pasku () w VS IDE i wybranie opcji Przejdź do definicji przeniesie Cię we właściwe miejsce.
Jeff Martin

9
Jedyną zaletą 98% konstrukcji językowych jest czytelność kodu. Wszystko, co jest poza operatorem oddziału, etykietą, goto i zwiększeniem, ma nieco ułatwić Ci życie.
Giovanni Galbo

2
To nie do końca prawda, są sytuacje, w których metody rozszerzające są jedynym sposobem na rozszerzenie klasy. Poza klasami zamkniętymi używam ich w dość wyjątkowej sytuacji. Zobacz mój post poniżej.
Edwin Jarvis

34

Nie zapomnij o narzędziach! Kiedy dodasz metodę rozszerzającą M do typu Foo, otrzymasz „M” na liście Intellisense Foo (zakładając, że klasa rozszerzenia jest w zakresie). To sprawia, że ​​„M” jest znacznie łatwiejsze do znalezienia niż MyClass.M (Foo, ...).

W końcu to tylko cukier syntaktyczny do stosowania w innych metodach statycznych, ale jak kupno domu: „lokalizacja, lokalizacja, lokalizacja!” Jeśli wisi na typie, ludzie go znajdą!


2
Jest też pewien minus: metody rozszerzeń (takie jak te z System.Linq wypełniają autouzupełnianie IntelliSense dłuższą listą, co utrudnia nawigację.
Jared Updike

@Jared: ... ale tylko wtedy, gdy zaimportowano przestrzeń nazw, w której znajdują się metody rozszerzające, więc w rzeczywistości masz niewielki stopień kontroli nad tym „zanieczyszczeniem IntelliSense”. Po drugie, nie zapominajmy, że istnieją klasy w BCL (takie jak String), które mają dość długą listę metod instancji, więc ten problem nie jest specyficzny dla metod rozszerzających.
stakx - nie publikuje już

29

Dwie kolejne zalety metod rozszerzania, z którymi się spotkałem:

  • Interfejs płynny można zamknąć w statycznej klasie metod rozszerzających, uzyskując w ten sposób oddzielenie problemów między klasą podstawową a jej płynnymi rozszerzeniami; Widziałem, że osiągają większą łatwość konserwacji.
  • Metody rozszerzające można zawiesić na interfejsach, umożliwiając w ten sposób określenie kontraktu (za pośrednictwem interfejsu) i powiązanej serii zachowań opartych na interfejsach (za pomocą metod rozszerzających), ponownie oferując oddzielenie problemów. Przykładem są metody rozszerzenie LINQ podoba Select(...), Where(...)itp Hung wyłączyć IEnumerable<T>interfejs.

1
Czy możesz zacytować kilka przykładów kodu dla obu punktów, aby pomóc im lepiej zrozumieć? Wydaje się, że to ciekawy punkt widokowy.
RBT

tak, przykład dla drugiego punktu byłby miły.
Ini

26

Niektóre z najlepszych zastosowań metod rozszerzających to:

  1. Rozszerz funkcjonalność na obiekty osób trzecich (zarówno komercyjnych, jak i wewnętrznych mojej firmy, ale zarządzane przez oddzielną grupę), które w wielu przypadkach będą oznaczone jako sealed.
  2. Twórz domyślne funkcje dla interfejsów bez konieczności implementowania klasy abstrakcyjnej

Weźmy na przykład IEnumerable<T>. Chociaż jest bogaty w metody rozszerzające, denerwowało mnie, że nie zaimplementował ogólnej metody ForEach. Więc zrobiłem własne:

public void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
    foreach ( var o in enumerable )
    {
        action(o);
    }
}

Voila, wszystkie moje IEnumerable<T>obiekty bez względu na typ implementacji i to, czy je napisałem, czy też ktoś inny miał teraz ForEachmetodę, dodając odpowiednią instrukcję „using” w moim kodzie.


3
+1 Uwielbiam to rozszerzenie, napisałem takie właśnie. Tak bardzo przydatne
Maslow

10
Zwróć uwagę, że metoda musi być statyczna. Proponuję przeczytać blogs.msdn.com/ericlippert/archive/2009/05/18/… przed podjęciem decyzji o użyciu tego rozszerzenia.
TrueWill

Powód, dla którego implementacja metody rozszerzenia ForEach w IEnumerable nie jest doceniana - blogs.msdn.microsoft.com/ericlippert/2009/05/18/…
RBT

12

Jednym z wielkich powodów używania metod rozszerzających jest LINQ. Bez metod rozszerzających wiele z tego, co można zrobić w LINQ, byłoby bardzo trudnych. Metody rozszerzenia Where (), Contains (), Select oznaczają, że do istniejących typów dodaje się znacznie więcej funkcji bez zmiany ich struktury.


4
to nie jest „jeden z”, jest to główny powód dla metod rozszerzających.
gbjbaanb

3
Jak to? Jest to jeden z wielu powodów, dla których istnieją metody rozszerzające, a nie jedyny wymóg. Mogę używać metod rozszerzających bez użycia LINQ.
Ray Booysen

Myślę, że miał na myśli, że to był powód ich stworzenia, ale tak, sprawia, że ​​brzmi to tak, jakby były dla nich jedynym właściwym zastosowaniem.
Brent Rittenhouse

9

Odpowiedzi na temat zalet metod rozszerzeń jest wiele; co powiesz na rozwiązanie problemów ?

Największą wadą jest to, że nie ma błędu kompilatora ani ostrzeżenia, jeśli masz zwykłą metodę i metodę rozszerzającą z tym samym podpisem w tym samym kontekście.

Załóżmy, że tworzysz metodę rozszerzającą mającą zastosowanie do określonej klasy. Później ktoś tworzy metodę z identycznym podpisem na samej tej klasie.

Twój kod się skompiluje i możesz nawet nie otrzymać błędu wykonania. Ale nie używasz już tego samego kodu co wcześniej.


1
4. Zastanów się dwa razy, zanim rozszerzysz typy, których nie jesteś właścicielem . Jeśli piszesz tylko metody rozszerzające dla typów, które posiadasz, nigdy nie musisz się martwić, że rozszerzenia zostaną uszkodzone przez zmiany w rozszerzanym typie. Z drugiej strony, jeśli rozszerzasz inne typy ludów, to jesteś zasadniczo na ich łasce.
DavidRR

1
... Alternatywnie, możesz zminimalizować to ryzyko, wybierając nazwy, które prawdopodobnie nie będą używane przez kogoś innego, lub minimalizując całkowitą liczbę zdefiniowanych przez Ciebie metod rozszerzania (tj. Zmniejszając powierzchnię). Jedną z technik, aby to zrobić, może być napisanie jednej metody rozszerzającej, która konwertuje podstawowy obiekt na inny typ, który jest kontrolowany przez Ciebie.
DavidRR

Metody rozszerzeń (przewodnik programowania w języku C #) : Ogólnie rzecz biorąc, prawdopodobnie będziesz wywoływać metody rozszerzające znacznie częściej niż implementować własne.
DavidRR


4

Mój osobisty argument za metodami rozszerzającymi jest taki, że bardzo dobrze pasują do projektu OOP: rozważ prostą metodę

bool empty = String.IsNullOrEmpty (myString)

w stosunku do

bool empty = myString.IsNullOrEmpty ();

Nie rozumiem, co twój przykład ma wspólnego z OOP. Co masz na myśli?
Andrew Hare

1
"wydaje się", że metoda należy do obiektu
Bluenuance

3
To jest zły przykład (może wyrzucić NullReferenceException), ale jest na dobrej drodze. Lepszym przykładem może być zamiana Math.abs (-4) na coś takiego jak -4.abs ();
Outlaw Programmer

15
zgodnie z moją wiedzą (i moim doświadczeniem, jeśli pamięć służy), myString.IsNullOrEmpty () nie musi zgłaszać wyjątku NullReferenceException, ponieważ metody rozszerzające nie wymagają wystąpienia obiektu w celu uruchomienia.
David Alpert

1
Osobiście nie jestem fanem metod rozszerzających, które są przeznaczone do pracy z pustą instancją - dla czytelnika wygląda to na jedną rzecz, ale robi coś innego.
Mark Simpson

4

Powyżej znajduje się mnóstwo świetnych odpowiedzi na temat tego, jakie metody rozszerzenia pozwalają.

Moja krótka odpowiedź brzmi - prawie eliminują potrzebę tworzenia fabryk.

Zaznaczę tylko, że nie są to nowe koncepcje, a jednym z największych ich potwierdzeń jest to, że są zabójczą funkcją w Celu-C ( kategorie ). Dodają tak dużą elastyczność do programowania opartego na frameworku, że NeXT miał głównych użytkowników zajmujących się modelowaniem finansowym z NSA i Wall Street.

REALbasic również je wdraża jako rozszerzenie metod i mają one podobne zastosowanie, upraszczając rozwój.


4

Chciałbym poprzeć inne odpowiedzi tutaj, które wspominają o poprawie czytelności kodu jako ważnym powodem stojącym za metodami rozszerzającymi. Pokażę to w dwóch aspektach: łączenie metod w łańcuchy vs. wywołania metod zagnieżdżonych oraz zaśmiecanie zapytania LINQ bezsensownymi statycznymi nazwami klas.


Weźmy to zapytanie LINQ jako przykład:

numbers.Where(x => x > 0).Select(x => -x)

Obie Wherei Selectsą metodami rozszerzającymi zdefiniowanymi w klasie statycznej Enumerable. Tak więc, gdyby nie istniały metody rozszerzające, a byłyby to normalne metody statyczne, ostatni wiersz kodu musiałby zasadniczo wyglądać następująco:

Enumerable.Select(Enumerable.Where(numbers, x => x > 0), x => -x)

Zobacz, o ile bardziej nieprzyjemne stało się to zapytanie.


Po drugie, gdybyś chciał teraz wprowadzić własny operator zapytania, naturalnie nie miałbyś możliwości zdefiniowania go wewnątrz Enumerableklasy statycznej, tak jak wszystkie inne standardowe operatory zapytań, ponieważ Enumerableznajduje się on we frameworku i nie masz kontroli nad tą klasą. Dlatego musiałbyś zdefiniować własną klasę statyczną zawierającą metody rozszerzające. Możesz wtedy otrzymać zapytania, takie jak to:

Enumerable.Select(MyEnumerableExtensions.RemoveNegativeNumbers(numbers), x => -x)
//                ^^^^^^^^^^^^^^^^^^^^^^
//                different class name that has zero informational value
//                and, as with 'Enumerable.xxxxxx', only obstructs the
//                query's actual meaning.

3

To prawda, że ​​możesz dodać swoją metodę (rozszerzenia) bezpośrednio do swojej klasy. Ale nie wszystkie zajęcia są napisane przez Ciebie. Klasy z biblioteki podstawowej lub bibliotek innych firm są często zamknięte i uzyskanie cukru syntetycznego bez metod rozszerzających byłoby niemożliwe. Pamiętaj jednak, że metody rozszerzające są jak (statyczne) samodzielne metody w np. c ++


3

Metody rozszerzające mogą również pomóc w utrzymaniu czystości klas i zależności klas. Na przykład, możesz potrzebować metody Bar () dla klasy Foo wszędzie tam, gdzie używane jest Foo. Możesz jednak potrzebować metody .ToXml () w innym zestawie i tylko dla tego zestawu. W takim przypadku można dodać niezbędne zależności System.Xml i / lub System.Xml.Linq w tym zestawie, a nie w oryginalnym zestawie.

Korzyści: zależności w definiowanym zestawie klas są zredukowane tylko do podstawowych potrzeb, a inne zużywane zestawy nie będą mogły używać metody ToXml (). Więcej informacji można znaleźć w tej prezentacji kontrolera PDC .


3

Zgadzam się, że metody rozszerzające zwiększają czytelność kodu, ale tak naprawdę to nic innego jak statyczne metody pomocnicze.

IMO używające metod rozszerzających do dodawania zachowania do twoich klas może być:

Mylące: programiści mogą uważać, że metody są częścią typu rozszerzonego, nie rozumiejąc, dlaczego metody zniknęły, gdy przestrzeń nazw rozszerzeń nie została zaimportowana.

Antywzór: decydujesz się dodać zachowanie do typów w swoim frameworku za pomocą metod rozszerzających, a następnie wysyłasz je do kogoś, kto zajmuje się testowaniem jednostkowym. Teraz utknął w strukturze zawierającej kilka metod, których nie może sfałszować.


Nie jest prawdą, że programista ma kilka metod, których nie może sfałszować. Te metody rozszerzające ostatecznie wywołują jakąś metodę w klasie / interfejsie, który rozszerzają, i ta metoda może zostać przechwycona, biorąc pod uwagę, że jest wirtualna.
Patrik Hägne

1
Cóż, to zależy od tego, co jest zrobione w metodzie rozszerzenia. Powiedzmy, że używasz udostępnionych metod rozszerzających w interfejsie, nawet nie wiedząc, że są to metody rozszerzające. Wtedy nie chcesz sfałszować wywołania metody do interfejsu, ale zdajesz sobie sprawę, że w rzeczywistości wywołujesz metodę statyczną. Wywołanie metody statycznej nie może zostać przechwycone przez fałszywy interfejs API oparty na proxy, więc jedyną opcją jest zastanowienie się nad metodą rozszerzającą, aby dowiedzieć się, które metody faktycznie fałszywe. Wciąż przeciwny w połączeniu z testami jednostkowymi IMO.
HaraldV

2

Metody rozszerzające są tak naprawdę włączeniem .NET refaktora „Wprowadzanie metody obcej” z książki Martina Fowlera (aż do podpisu metody). Mają w zasadzie te same korzyści i pułapki. W sekcji dotyczącej tego refaktora mówi, że są one obejściem, kiedy nie można zmodyfikować klasy, która powinna być właścicielem metody.


2
+1, ale pamiętaj, że możesz używać metod rozszerzających również z interfejsami.
TrueWill,

2

Metody rozszerzające postrzegam głównie jako przyznanie, że być może nie powinny one blokować wolnych funkcji.

W społeczności C ++ za dobrą praktykę OOP uważa się preferowanie bezpłatnych funkcji niebędących członkami, ponieważ te funkcje nie przerywają hermetyzacji, uzyskując dostęp do niepotrzebnych członków prywatnych. Wydaje się, że metody rozszerzeń są okrężną drogą do osiągnięcia tego samego. Oznacza to czystszą składnię dla funkcji statycznych, które nie mają dostępu do prywatnych elementów członkowskich.

Metody przedłużające to nic innego jak cukier syntaktyczny, ale nie widzę żadnej szkody w ich stosowaniu.


2
  • Inteligencja na samym obiekcie zamiast wywoływania jakiejś brzydkiej funkcji użyteczności
  • W przypadku funkcji konwersji można zmienić „XToY (X x)” na „ToY (ten X x)”, co daje ładny x.ToY () zamiast brzydkiego XToY (x).
  • Rozszerzaj klasy, nad którymi nie masz kontroli
  • Rozszerz funkcjonalność klas, gdy niepożądane jest dodawanie metod do samych klas. Na przykład można zachować proste i pozbawione logiki obiekty biznesowe oraz dodać określoną logikę biznesową z brzydkimi zależnościami w metodach rozszerzających

2

Używam ich do ponownego użycia klas modeli obiektów. Mam kilka klas, które reprezentują obiekty, które mam w bazie danych. Te klasy są używane po stronie klienta tylko do wyświetlania obiektów, więc podstawowym zastosowaniem jest dostęp do właściwości.

public class Stock {
   public Code { get; private set; }
   public Name { get; private set; }
}

Z tego powodu nie chcę mieć metod logiki biznesowej w tych klasach, więc każdą logikę biznesową tworzę jako metodę rozszerzającą.

public static class StockExtender {
    public static List <Quote> GetQuotesByDate(this Stock s, DateTime date)
    {...}
}

W ten sposób mogę używać tych samych klas do przetwarzania logiki biznesowej i do wyświetlania interfejsu użytkownika bez przeciążania strony klienta niepotrzebnym kodem.

Interesującą rzeczą w tym rozwiązaniu jest to, że moje klasy modeli obiektów są generowane dynamicznie przy użyciu Mono.Cecil , więc dodanie metod logiki biznesowej byłoby bardzo trudne, nawet gdybym chciał. Mam kompilator, który czyta pliki definicji XML i generuje te klasy pośredniczące reprezentujące jakiś obiekt, który mam w bazie danych. Jedynym podejściem w tym przypadku jest ich rozszerzenie.


1
Możesz użyć do tego klas częściowych ...
JJoos


1

W moim ostatnim projekcie użyłem metody rozszerzającej, aby dołączyć metody Validate () do obiektów biznesowych. Uzasadniłem to, ponieważ obiekty biznesowe, w których można serializować obiekty transferu danych i będą używane w różnych domenach, tak jak w przypadku ogólnych podmiotów e-commerce, takich jak produkt, klient, sprzedawca itp. Cóż, w różnych domenach reguły biznesowe mogą być różne, więc logika walidacji z późnym wiązaniem w metodzie Validate dostosowana do klasy bazowej moich obiektów transferu danych. Mam nadzieję, że to ma sens :)


1

Jednym z przypadków, w których metody rozszerzające były całkiem przydatne, był klient-aplikacja korzystająca z usług sieciowych ASMX. Ze względu na serializację zwracane typy metod internetowych nie zawierają żadnych metod (na kliencie dostępne są tylko właściwości publiczne tych typów).

Metody rozszerzające pozwoliły na dodanie funkcjonalności (po stronie klienta) do typów zwracanych przez metody internetowe bez konieczności tworzenia kolejnego modelu obiektowego lub wielu klas opakowujących po stronie klienta.


1

Metody rozszerzające mogą służyć do tworzenia pewnego rodzaju mieszanki w języku C #.

To z kolei zapewnia lepsze rozdzielenie problemów dla pojęć ortogonalnych. Spójrz na tę odpowiedź jako przykład.

Można to również wykorzystać do włączenia ról w języku C #, koncepcji centralnej dla architektury DCI .


1

Pamiętaj również, że metody rozszerzające zostały dodane, aby pomóc zapytaniom Linq być bardziej czytelnymi, gdy są używane w ich stylu C #.

Te dwie afekty są absolutnie równoważne, ale pierwsza jest znacznie bardziej czytelna (a luka w czytelności oczywiście wzrośnie wraz z większą liczbą metod połączonych łańcuchowo).

int n1 = new List<int> {1,2,3}.Where(i => i % 2 != 0).Last();

int n2 = Enumerable.Last(Enumerable.Where(new List<int> {1,2,3}, i => i % 2 != 0));

Zauważ, że w pełni kwalifikowana składnia powinna nawet wyglądać następująco:

int n1 = new List<int> {1,2,3}.Where<int>(i => i % 2 != 0).Last<int>();

int n2 = Enumerable.Last<int>(Enumerable.Where<int>(new List<int> {1,2,3}, i => i % 2 != 0));

Przypadkowo parametry typu Wherei Lastnie muszą być jawnie wymieniane, ponieważ można je wywnioskować dzięki obecności pierwszego parametru z tych dwóch metod (parametr, który jest wprowadzany przez słowo kluczowe thisi czyni je metodami rozszerzającymi).

Ten punkt jest oczywiście zaletą (między innymi) metod rozszerzających i można z tego skorzystać w każdym podobnym scenariuszu, w którym występuje łączenie metod.

W szczególności jest to bardziej elegancki i przekonujący sposób, w jaki znalazłem metodę klasy bazowej wywoływaną przez dowolną podklasę i zwracającą silnie wpisane odniesienie do tej podklasy (z typem podklasy).

Przykład (ok, ten scenariusz jest totalnie kiepski): po dobrej nocy zwierzę otwiera oczy, po czym płacze; każde zwierzę otwiera oczy w ten sam sposób, podczas gdy pies szczeka i kwaks kaczka.

public abstract class Animal
{
    //some code common to all animals
}

public static class AnimalExtension
{
    public static TAnimal OpenTheEyes<TAnimal>(this TAnimal animal) where TAnimal : Animal
    {
        //Some code to flutter one's eyelashes and then open wide
        return animal; //returning a self reference to allow method chaining
    }
}

public class Dog : Animal
{
    public void Bark() { /* ... */ }
}

public class Duck : Animal
{
    public void Kwak() { /* ... */ }
}

class Program
{
    static void Main(string[] args)
    {
        Dog Goofy = new Dog();
        Duck Donald = new Duck();
        Goofy.OpenTheEyes().Bark(); //*1
        Donald.OpenTheEyes().Kwak(); //*2
    }
}

Koncepcyjnie OpenTheEyespowinna być Animalmetoda, ale to potem powrócić instancję klasy abstrakcyjnej Animal, która nie zna konkretnych metod podklasy jak Barklub Duckczy cokolwiek innego. Dwie linie skomentowane jako * 1 i * 2 spowodowałyby wtedy błąd kompilacji.

Ale dzięki metodom rozszerzającym możemy mieć coś w rodzaju „metody bazowej, która zna typ podklasy, w której jest wywoływana”.

Zauważ, że prosta metoda ogólna mogłaby wykonać zadanie, ale w znacznie bardziej niezręczny sposób:

public abstract class Animal
{
    //some code common to all animals

    public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
    {
        //Some code to flutter one's eyelashes and then open wide
        return (TAnimal)this; //returning a self reference to allow method chaining
    }
}

Tym razem nie ma parametru, a zatem nie ma możliwości wnioskowania o typie zwracanym. Wezwanie może być niczym innym jak:

Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();

... co może znacznie ważyć kod, jeśli w grę wchodzi więcej łańcuchów (zwłaszcza wiedząc, że parametr typu zawsze będzie <Dog>w wierszu Goofy'ego i <Duck>Donalda ...)


1
Pierwszy raz widziałem "kwaka". Czy to jest pisownia powszechna w innym kraju? Jedyny sposób, w jaki kiedykolwiek widziałem, to „szarlatan”.
Brent Rittenhouse

0

Mam tylko jedno słowo do powiedzenia na ten temat: UTRZYMANIE To jest klucz do korzystania z metod rozszerzających


4
Myślę, że możesz potrzebować więcej niż jednego słowa, ponieważ nie rozumiem, co to oznacza.
Outlaw Programmer

2
Właściwie powiedziałbym, że jest to argument przeciwko metodom rozszerzania. Jednym z zagrożeń związanych z metodami rozszerzania jest to, że mogą one być tworzone wszędzie i przez wszystkich. Dziki wzrost może się zdarzyć, jeśli nie jest używany ostrożnie.
Boris Callens

Możesz łatwo znaleźć wszystkie odniesienia, gdy używasz EM. Ponadto, aby globalnie zmienić jedną metodę, możesz to zrobić tylko z jednego miejsca
Tamir

0

Myślę, że metody rozszerzające pomagają napisać kod, który jest bardziej przejrzysty.

Zamiast umieszczać nową metodę wewnątrz swojej klasy, jak zasugerował twój przyjaciel, umieszczasz ją w przestrzeni nazw ExtensionMethods. W ten sposób utrzymujesz logiczne poczucie porządku w swojej klasie. Metody, które tak naprawdę nie zajmują się bezpośrednio twoją klasą, nie zaśmiecają jej.

Uważam, że metody rozszerzające sprawiają, że kod jest bardziej przejrzysty i lepiej zorganizowany.



0

Uwielbiam je za tworzenie html. Często istnieją sekcje, które są używane wielokrotnie lub generowane rekurencyjnie, w których funkcja jest przydatna, ale w przeciwnym razie zakłóciłaby przepływ programu.

        HTML_Out.Append("<ul>");
        foreach (var i in items)
            if (i.Description != "")
            {
                HTML_Out.Append("<li>")
                    .AppendAnchor(new string[]{ urlRoot, i.Description_Norm }, i.Description)
                    .Append("<div>")
                    .AppendImage(iconDir, i.Icon, i.Description)
                    .Append(i.Categories.ToHTML(i.Description_Norm, urlRoot)).Append("</div></li>");
            }

        return HTML_Out.Append("</ul>").ToString();

Istnieją również sytuacje, w których obiekt wymaga niestandardowej logiki do przygotowania wyjścia HTML - metody rozszerzające pozwalają na dodanie tej funkcji bez mieszania prezentacji i logiki w klasie.


0

Odkryłem, że metody rozszerzające są przydatne do dopasowywania zagnieżdżonych argumentów ogólnych.

To brzmi trochę dziwnie - ale powiedzmy, że mamy klasę ogólną MyGenericClass<TList>i wiemy, że sam TList jest ogólny (np. A List<T>), nie sądzę, że istnieje sposób na wykopanie zagnieżdżonego „T” z listy bez żadnego rozszerzenia metody lub statyczne metody pomocnicze. Jeśli mamy do dyspozycji tylko statyczne metody pomocnicze, jest to (a) brzydkie i (b) zmusi nas do przeniesienia funkcjonalności należącej do klasy do lokalizacji zewnętrznej.

np. aby pobrać typy w krotce i przekonwertować je na sygnaturę metody możemy skorzystać z metod rozszerzających:

public class Tuple { }
public class Tuple<T0> : Tuple { }
public class Tuple<T0, T1> : Tuple<T0> { }

public class Caller<TTuple> where TTuple : Tuple { /* ... */ }

public static class CallerExtensions
{
     public static void Call<T0>(this Caller<Tuple<T0>> caller, T0 p0) { /* ... */ }

     public static void Call<T0, T1>(this Caller<Tuple<T0, T1>> caller, T0 p0, T1 p1) { /* ... */ }
}

new Caller<Tuple<int>>().Call(10);
new Caller<Tuple<string, int>>().Call("Hello", 10);

To powiedziawszy, nie jestem pewien, gdzie powinna znajdować się linia podziału - kiedy metoda powinna być metodą rozszerzającą, a kiedy statyczną metodą pomocniczą? jakieś pomysły?


0

Mam na ekranie strefy wprowadzania danych i wszystkie muszą implementować standardowe zachowanie, niezależnie od ich dokładnego typu (pola tekstowe, pola wyboru itp.). Nie mogą dziedziczyć wspólnej klasy bazowej, ponieważ każdy typ strefy wejściowej pochodzi już z określonej klasy (TextInputBox itp.)

Może wchodząc w górę w hierarchii dziedziczenia, mógłbym znaleźć wspólnego przodka, jak powiedzmy WebControl, ale nie opracowałem klasy frameworkowej WebControl i nie ujawnia tego, czego potrzebuję.

Dzięki metodzie przedłużenia mogę:

1) Rozszerz klasę WebControl, a następnie uzyskaj moje ujednolicone standardowe zachowanie dla wszystkich moich klas wejściowych

2) alternatywnie uczyń wszystkie moje klasy pochodnymi z interfejsu, powiedzmy IInputZone, i rozszerz ten interfejs metodami. Teraz będę mógł wywoływać metody rozszerzeń związane z interfejsem we wszystkich moich strefach wejściowych. W ten sposób osiągnąłem rodzaj wielokrotnego dziedziczenia, ponieważ moje strefy wejściowe już pochodzą z wielu klas bazowych.


0

Jest tak wiele wspaniałych przykładów metod rozszerzających… zwłaszcza w IEnumerables, jak opisano powyżej.

np. jeśli mam metodę IEnumerable<myObject>mogę utworzyć i rozszerzyć dlaIEnumerable<myObject>

mylist List<myObject>;

... utwórz listę

mylist.DisplayInMyWay();

Bez metod rozszerzenia musiałby wywołać:

myDisplayMethod(myOldArray); // can create more nexted brackets.

innym świetnym przykładem jest błyskawiczne utworzenie okrągłej listy połączonej!

Mogę się za to pochwalić!

lista okrężna połączona przy użyciu metod rozszerzających

Teraz połącz je i używając kodu rozszerzającego Methods odczytuje się w następujący sposób.

myNode.NextOrFirst().DisplayInMyWay();

zamiast

DisplayInMyWay(NextOrFirst(myNode)).

korzystanie z metod rozszerzających Jest schludniejszy i łatwiejszy do odczytania oraz bardziej zorientowany obiektowo. również bardzo blisko:

myNode.Next.DoSomething()

Pokaż to swojemu koledze! :)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.