2012-12-28 8 views
9

Niedawno bawiłem się z pomysłem zastosowania metod rozszerzających do implementacji narzędzi pomocniczych na klasach, które kontroluję (tj. Są w tym samym programie i mogę modyfikować). Powodem tego jest to, że wiele razy te programy pomocnicze są używane w bardzo specyficznych sytuacjach i nie wymagają dostępu do klas wewnętrznych wartości.Czy dopuszczalne jest stosowanie metod rozszerzenia dla klasy, którą można zmodyfikować?

Na przykład załóżmy, że mam klasę StackExchange. Będzie miał metody takie jak PostQuestion i Search i AnswerQuestion.

Co, jeśli chciałbym ręcznie obliczyć swoją reputację, aby upewnić się, że StackOverflow mnie nie zdradza. Chciałbym zaimplementować coś wzdłuż linii:

int rep=0; 
foreach(var post in StackExchangeInstance.MyPosts) 
{ 
    rep+=post.RepEarned; 
} 

mogę dodać metodę do klasy Stack Exchange Network, ale nie wymaga żadnych wewnętrzne, i jest używany tylko z jednego lub dwóch innych części programu .

Teraz wyobraź sobie, że zamiast tego masz 10 lub 20 takich specyficznych metod pomocniczych. Przydatny w pewnym scenariuszu na pewno, ale zdecydowanie nie w ogólnym przypadku. Mój pomysł zmienia się coś

public static RepCalcHelpers 
{ 
    public static int CalcRep(StackExchange inst){ ... } 
} 

do czegoś

namespace Mynamespace.Extensions.RepCalculations 
{ 
    public static RepCalcExtensions 
    { 
    public static int CalcRep(this Stackexchange inst){...} 
    } 
} 

Uwaga nazw. Najlepiej byłoby użyć tego do grupowania metod rozszerzenia w określonym scenariuszu. Na przykład "Wyliczenia", "Statystyka" itp.

Próbowałem wyszukać, czy ten wzorzec jest w ogóle słyszalny, i nie znalazłem żadnego dowodu na to, że metody rozszerzające są używane do innych niż klas nie można modyfikować.

Jakie są niedociągnięcia w tym "wzorze"? Czy zamiast tego powinienem trzymać się dziedziczenia lub kompozycji, lub po prostu dobrej klasy statycznej pomocnika?

+0

Metody rozszerzeń mają być stosowane, gdy nie można zmodyfikować oryginalnej klasy. Jeśli to ty wykonujesz zajęcia, nie widzę wartości dodanej. Jeśli chcesz tylko umieścić metody w oddzielnym pliku kodu, możesz użyć słowa kluczowego 'partial'. –

+0

Po to, by wyjaśnić, zrobiłbyś to, aby ukryć pewne funkcje, nie uwzględniając konkretnych instrukcji 'using', które posiadają metody rozszerzenia krawędzi. Bardzo interesujące! –

+0

Możesz sprawdzić link [to] (http://stackoverflow.com/a/551600/649524), który określa zalety i wady metod rozszerzeń. – Tilak

Odpowiedz

4

Przeczytałbym część Wytycznych do projektowania ramowego dotyczących metod rozszerzania. Here to post jednego z autorów drugiego wydania. Przypadek użycia, który opisujesz (wyspecjalizowane metody pomocnicze) jest cytowany przez Phila Haacka jako poprawne użycie dla metod rozszerzenia z tą wadą, że wymaga dodatkowej wiedzy o API, aby znaleźć te "ukryte" metody.

Nie wspomniano w tym poście, ale zalecane w tej książce jest to, że metody rozszerzeń przechodzą do oddzielnej przestrzeni nazw z rozszerzonej klasy. W przeciwnym razie zawsze będą pojawiać się z intellisense i nie ma sposobu, aby je wyłączyć.

+0

Cóż, spójność, którą zakładam, jest twoją przyjaciółką, aby zapewnić ich wykrywalność. Posiadanie wspólnej przestrzeni nazw '.Extensions. *' Zakładam, że będzie to dość łatwe. – Earlz

+0

@Ellz Consistency to zdecydowanie plus. Zważam to na to, że nie muszę dodawać wielu instrukcji 'using', żeby zrobić coś użytecznego. –

+0

Cóż, najlepiej nie dodawać więcej niż dwóch rozszerzeń za pomocą instrukcji, więc trudno byłoby wyważyć, jak grubo są pogrupowane rozszerzenia. – Earlz

0

Myślę, że widziałem ten wzorzec gdzie indziej. Może to być dość mylące, ale także dość potężne. W ten sposób możesz udostępnić klasę w bibliotece i zestaw metod rozszerzeń w oddzielnej przestrzeni nazw. Każdy, kto korzysta z Twojej biblioteki, może zaimportować przestrzeń nazw za pomocą metod rozszerzeń lub udostępnić własne metody rozszerzenia.

Dobrym kandydatem do tego wzoru będzie, jeśli masz kilka metod rozszerzenia używanych tylko do testowania jednostkowego (np. Do porównywania, czy dwa obiekty są takie same, jak w przypadku testów jednostkowych).

0

Wydaje się, że dokonujesz porównania, że ​​metoda rozszerzenia jest równoważna z metodą instancji publicznej. Tak naprawdę nie jest.

Metoda rozszerzenia jest po prostu publiczną metodą narzędzia statycznego, która ma wygodniejszą składnię do wywoływania.

Tak więc najpierw musimy zadać sobie pytanie, czy metoda ta może być metodą instancji samej klasy, czy jest bardziej odpowiednia dla statycznej metody klasy zewnętrznej. Fakt, że bardzo niewielu użytkowników klasy potrzebuje tej funkcjonalności, ponieważ jest wysoce zlokalizowane i nie jest prawdziwie zachowaniem, które sama klasa wykonuje, ale raczej zachowanie wykonywane na klasie przez zewnętrzny podmiot oznacza, że ​​jest odpowiednie, aby było statyczne. Podstawową wadą jest to, że zachowanie jest potencjalnie trudniejsze do znalezienia, jeśli ktoś ma User i chce ponownie przeliczyć ich przedstawicieli. Teraz, w tym konkretnym przypadku jest trochę na ogrodzeniu i możesz pójść w drugą stronę, ale pochylam się w kierunku metody statycznej.

Teraz, gdy zdecydowaliśmy, że powinno być statyczne, jest to całkowicie oddzielna kwestia, czy powinna to być metoda rozszerzająca, czy nie. Jest to o wiele bardziej subiektywne i wchodzi w zakres osobistych preferencji. Czy metody mogą być powiązane? Jeśli tak, to metody rozszerzenia łańcuchują się o wiele ładniej niż zagnieżdżone wywołania metod statycznych. Czy jest prawdopodobne, że będzie dużo używany w plikach, które z niego korzystają? Jeśli tak, metody rozszerzeń mogą nieco uprościć kod, jeśli nie, to naprawdę nie pomaga tak bardzo, a nawet boli. Do przykładu zabawkowego pewnie bym powiedział, że osobiście tego nie zrobiłbym, ale nie miałbym żadnego problemu z kimś, kto to zrobił (w końcu nadal można użyć metody rozszerzającej tak, jakby była to zwykła, publiczna metoda statyczna). W przypadku przykładu innego niż zabawka jest to głównie decyzja indywidualna. Kluczową kwestią jest uważać, jakie klasy rozszerzasz, i zadać sobie pytanie, czy użytkownik jest skłonny do zaśmiecania IntelliSense typu, aby wywoływać metody nieco wygodniej (to znowu wraca do tego, ile jest zużywane na plik, którego używa w).

Warto również wspomnieć, że istnieje kilka przypadków skrajnych, w których metody rozszerzeń mogą być bardziej zaawansowane niż instancyjne metody .. W szczególności poprzez wykorzystanie wnioskowania o typie. W przypadku zwykłej metody instancji można łatwo zaakceptować typ lub podtyp tego typu, ale czasami warto zwrócić to, co typ został przekazany zamiast typu nadrzędnego. Jest to szczególnie przydatne w płynnych interfejsach API. Nie jest to jednak bardzo powszechny przykład i jest tylko luźno związany z pańskim pytaniem, więc nie będę się nad tym rozwodził.

+0

Dla całej metody statycznej versus bit rozszerzenia: w tym konkretnym przypadku tak naprawdę Myślę, że metody rozszerzeń są łatwiejsze do wykrycia, zakładając, że używasz intellisense. Najpierw zajrzyj do przestrzeni nazw rozszerzeń, aby znaleźć coś odpowiedniego dla tego, co robisz, a następnie metody, które chcesz, są automatycznie dodawane do odpowiedniej klasy. Nie mogę powiedzieć, ile razy musiałem szukać statycznych klas pomocniczych, zwłaszcza gdy są one w różnych niespójnych przestrzeniach nazw – Earlz

0

Metody rozszerzeń mogą być bardzo przydatne w przypadkach, w których klasa implementuje interfejs i użytkownik chce uniknąć konieczności stosowania tej samej metody na innych "przyszłych" klasach, które implementują ten sam interfejs. Na przykład, StackExchange implementuje IStackExchange i ProgrammersExchange również implementuje IStackExchange. Twoja przykładowa metoda rozszerzenia byłaby przydatna do jednorazowego wdrożenia narzędzia CalcRep i nie trzeba go ponownie implementować w obu klasach. To jest właśnie powód dla wszystkich metod rozszerzenia obecnych w statycznej klasie Enumerable.

Poza tym nie widzę powodu, dla którego warto stosować metody rozszerzeń w klasie, którą można już modyfikować. Jeśli cokolwiek, ma to tę wadę, że jest uważane za spóźnione w procesie rozwiązywania przeciążenia.

+0

Nie sądzę, że program pomocniczy uważany za opóźniony w przypadku przeciążenia jest złym, w większości przypadków przypadki. Wygląda na Linq, na pewno istnieje metoda "IEnumerable .Count()', ale wiele rzeczy, które implementują IEnumerable, ma znacznie szybsze sposoby zliczania niż iterowanie na całej liście. – Earlz

+0

Metoda Enumerable.Count jest dobrze dostosowana do rozszerzenia metody z tego powodu, o którym wspomniałem na początku, a nie dlatego, że jest późno w rezerwie przeładowania. Nawet jeśli był on częścią interfejsu, każdy wdrażający mógłby wdrożyć własną szybką metodę liczenia. –

+1

... W rzeczywistości metoda Count() LINQ, zgodnie z obecnie wdrożoną wersją, dokonuje pewnych kontroli typów i odracza się do ICollection.Count, jeśli źródłowy wyliczalny jest ICollection. Metoda, która pobiera predykat, nie wykonuje tej kontroli, a wszelkie użycie funkcji Count() na końcu dodatkowego rozszerzenia Linq będzie miało wartość IEnumerable, która nie jest ICollection. – KeithS

Powiązane problemy