„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?
c#
extension-methods
Jerry
źródło
źródło
Extensions.To(1, 10)
jest bez znaczenia i1.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 przypadekdynamic
.Odpowiedzi:
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ć:
Czystsze i prostsze.
źródło
LongToFileSize(fileSize)
, co jest równie zwięzłe i prawdopodobnie równie jasne.Tylko zaletą metod rozszerzenie jest czytelność kodu. Otóż to.
Metody rozszerzające pozwalają na to:
zamiast tego:
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.
źródło
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ą!
źródło
String
), które mają dość długą listę metod instancji, więc ten problem nie jest specyficzny dla metod rozszerzających.Dwie kolejne zalety metod rozszerzania, z którymi się spotkałem:
Select(...)
,Where(...)
itp Hung wyłączyćIEnumerable<T>
interfejs.źródło
Niektóre z najlepszych zastosowań metod rozszerzających to:
sealed
.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ł terazForEach
metodę, dodając odpowiednią instrukcję „using” w moim kodzie.źródło
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.
źródło
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.
źródło
Płynne interfejsy i wrażliwość na kontekst, jak zademonstrował Greg Young na CodeBetter
źródło
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 ();
źródło
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.
źródło
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
Where
iSelect
są metodami rozszerzającymi zdefiniowanymi w klasie statycznejEnumerable
. 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
Enumerable
klasy statycznej, tak jak wszystkie inne standardowe operatory zapytań, ponieważEnumerable
znajduje 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.
źródło
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 ++
źródło
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 .
źródło
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ć.
źródło
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.
źródło
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.
źródło
źródło
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.
źródło
Pozwala C # lepiej obsługiwać języki dynamiczne, LINQ i kilkanaście innych rzeczy. Przeczytaj artykuł Scotta Guthrie .
źródło
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 :)
źródło
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.
źródło
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 .
źródło
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
Where
iLast
nie 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 kluczowethis
i 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
OpenTheEyes
powinna byćAnimal
metoda, ale to potem powrócić instancję klasy abstrakcyjnejAnimal
, która nie zna konkretnych metod podklasy jakBark
lubDuck
czy 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:
... 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 ...)źródło
Mam tylko jedno słowo do powiedzenia na ten temat: UTRZYMANIE To jest klucz do korzystania z metod rozszerzających
źródło
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.
źródło
Pozwala edytorowi / IDE na inteligentne automatyczne uzupełnianie sugestii.
źródło
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.
źródło
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. AList<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?
źródło
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.
źródło
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>
... utwórz listę
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.
zamiast
korzystanie z metod rozszerzających Jest schludniejszy i łatwiejszy do odczytania oraz bardziej zorientowany obiektowo. również bardzo blisko:
Pokaż to swojemu koledze! :)
źródło