2009-09-21 17 views
10

OK, mam nadzieję, że społeczność jako całość pomoże nam w rozwiązaniu debaty w miejscu pracy, która trwa już od jakiegoś czasu. Ma to związek z definiowaniem interfejsów, które akceptują lub zwracają listy pewnego rodzaju. Istnieje kilka sposobów, aby to zrobić:Które wolisz dla interfejsów: T [], IEnumerable <T>, IList <T> lub inne?

public interface Foo 
{ 
    Bar[] Bars { get; } 
    IEnumerable<Bar> Bars { get; } 
    ICollection<Bar> Bars { get; } 
    IList<Bar> Bars { get; } 
} 

Moja własna preferencja jest użycie IEnumerable dla argumentów i tablic dla wartości zwracanych:

public interface Foo 
{ 
    void Do(IEnumerable<Bar> bars); 
    Bar[] Bars { get; } 
} 

Mój argument za tym podejściem jest to, że klasa realizacja może utworzyć Lista bezpośrednio z IEnumerable i po prostu zwróć ją za pomocą List.ToArray(). Jednak niektórzy uważają, że IList powinien zostać zwrócony zamiast tablicy. Problem, który mam tutaj, polega na tym, że teraz musisz ponownie skopiować go z ReadOnlyCollection przed powrotem. Opcja zwracania IEnumerable wydaje się kłopotliwa dla kodu klienta?

Czego używasz/preferujesz? (szczególnie w odniesieniu do bibliotek, które będą używane przez innych programistów spoza organizacji).

+1

Widziałem błędy moich sposobów. Jak wiele stanów poniżej jest oczywiste, programiści nie widzą właściwości T [] jako niezmiennie Myślę, że moje nadużywanie ich nauczyło mnie pewnych złych nawyków, nigdy naprawdę nie obchodziło mnie, czy zmodyfikowały matrycę, ponieważ to była kopia, ale zdałem sobie sprawę z zamieszania, które powoduje. Zwycięzca Myślę, że –

Odpowiedz

18

Moje preferencje to IEnumerable<T>. Każdy inny sugerowany interfejs daje wygląd umożliwiający konsumentowi modyfikację podstawowej kolekcji. To prawie na pewno nie jest to, co chcesz zrobić, ponieważ pozwala konsumentom w milczeniu modyfikować kolekcję wewnętrzną.

Kolejny dobry jeden IMHO, to ReadOnlyCollection<T>. Pozwala na wszystkie zabawne właściwości .Count i Indexer i jednoznacznie mówi konsumentowi "nie możesz modyfikować moich danych".

+3

+1 w każdej aplikacji, nad którą pracowałem, przynajmniej, "IEnumerable" to wszystko, czego potrzebujesz 98% czasu. –

+0

Nie mówię, że się mylisz, ale nie zakładasz, że nie jesteś retuning a tylko do odczytu lub płytkiej kopii (lub w inny sposób czyniące niezmienny)? – annakata

+1

@annakata, ale jak wyrazić to swoim klientom? Nie można po prostu spojrzeć na powrót powiedz IList i wiedzieć: 1) czy nie powinienem tego modyfikować, 2) czy modyfikacja jest wyłączona przez wyrzucenie (Słownik :: Wartości) lub 3) zwrócenie kompletnej kopii. Po prostu nie jest to oczywiste. Z drugiej strony, zwracając niezliczoną liczbę , dość jednoznacznie mówi "nie dotykasz moich danych". – JaredPar

15

nie wrócę tablic - są naprawdę straszny rodzaj powrotu do wykorzystania przy tworzeniu API - jeśli naprawdę potrzebują zmienny sekwencja używać interfejsu IList<T> lub ICollection<T> lub powrócić do betonu Collection<T> zamiast.

także Proponuję lekturę Arrays considered somewhat harmful Eric Lippert:

mam pytanie moralne od autora języka programowania podręczniki drugi dzień zainteresowanie moje opinie na temat czy początkujący programiści powinno się uczyć, jak korzystać z tablic.

Zamiast faktycznie odpowiedzieć pytanie, dałem mu długą listę moich opinii na temat tablic, jak używam tablic, jak oczekujemy tablice być wykorzystywany w przyszłości, i tak dalej. Ten dostaje trochę długo, ale podobnie jak Pascal, I nie miał czasu, aby go skrócić.

Zacznę od stwierdzenia, kiedy zdecydowanie nie należy używać tablic, a następnie wosk bardziej filozoficzna o przyszłości nowoczesnego programowania oraz roli tablicy w przyszłym świecie.

+1

Nie przeczytałem wcześniej tego linku lippert - +1 za to samo – annakata

+0

Ditto - dobrze czytać. –

+2

Dzięki za okrzyk. :) –

8

Do kolekcji majątkowych, które są indeksowane (i indeksy mają niezbędną znaczenie semantyczne), należy użyć ReadOnlyCollection<T> (tylko do odczytu) lub IList<T> (odczyt/zapis). Jest najbardziej elastyczny i ekspresyjny. W przypadku zbiorów nieindeksowanych użyj IEnumerable<T> (tylko do odczytu) lub ICollection<T> (odczyt/zapis).

Parametry metody powinny używać IEnumerable<T>, chyba że 1) trzeba dodać/usunąć pozycje do kolekcji (ICollection<T>) lub 2) wymagają indeksów dla potrzeb niezbędnych celów semantycznych (IList<T>). Jeśli metoda może skorzystać z dostępności indeksowania (takiej jak procedura sortowania), zawsze może użyć as IList<T> lub .ToList(), gdy to się nie powiedzie w implementacji.

+0

+1 za użycie tego, co jest odpowiednie dla scenariusza ... to naprawdę nie 1, aby rządzić, a następnie wszystkie ... inny sposób na powiedzenie ReadOnlyCollection vs. IEnumerable jest załadowany fabrycznie lub załadowany leniwy. – eglasius

1

Wolałbym IEnumerable, ponieważ jest to najwyższy poziom interfejsów, umożliwiający użytkownikowi końcowemu ponowne oddanie według własnego uznania. Mimo że może to zapewnić użytkownikowi minimalną funkcjonalność na początek (w zasadzie tylko wyliczenie), to wystarczyłoby, aby pokryć praktycznie każdą potrzebę, szczególnie z metodami rozszerzenia, ToArray(), ToList() itp.

1

Myślę, że o tym w zakresie pisania najbardziej przydatnego kodu: kodu, który może zrobić więcej.

Wstawiając te terminy, oznacza to, że akceptuję najsłabszy interfejs jako argumenty metody, ponieważ dzięki temu mój kod jest przydatny z większej liczby miejsc. W tym przypadku jest to IEnumerable<T>. Masz tablicę? Możesz zadzwonić do mojej metody. Masz listę? Możesz zadzwonić do mojej metody. Masz blok iteracyjny? Masz pomysł.

Oznacza to również, że podoba mi się moja metoda zwracania najskuteczniejszego najsilniejszego interfejsu, dzięki czemu kod oparty na metodzie może z łatwością zrobić więcej. W tym przypadku byłby to IList<T>. Zauważ, że nie oznacza to, że skonstruuję listę, aby móc ją zwrócić. To po prostu oznacza, że ​​jeśli I ma już niektóre z nich implementuje IList<T>, równie dobrze mogę z niego korzystać.

Zauważ, że jestem trochę niekonwencjonalny w odniesieniu do typów zwrotu. Bardziej typowym podejściem jest również zwracanie słabszych typów z metod, aby uniknąć zablokowania się w konkretnej implementacji.

+1

... jeśli mam już coś, co implementuje IList , równie dobrze mogę go użyć ... Tak, widzę twój punkt widzenia; może to jednak powodować dziwne efekty uboczne. Należy minimalnie zawinąć go w ReadOnlyCollection (). –

+1

@ csharptest.net, jeśli argumentujesz, że IList powinien być spakowany w ReadOnly Collection, dlaczego zwrócisz tablicę siebie, która jest daleka od readonly, po tym jak każdy element może zostać zastąpiony dowolną rzeczą przypisywaną do danego typu? –

+0

Masz rację tutaj; moje błędne przekonanie było takie, że byłoby oczywiste, że otrzymujesz kopię i nie ma sensu jej modyfikować. Przyznaję, że w tej dyskusji najwyraźniej się myliłem ... Nigdy nie przyszło mi do głowy, że ludzie robią "Foo.Bars [0] = x" lub takie bzdury. W każdym razie dzięki za pomoc w oświeceniu mnie +1 :) –

0

IEnumerable < T> jest bardzo przydatny do leniwej oceny iteracji, szczególnie w scenariuszach, które używają łańcuchów metod.

Ale jako typ zwrotu dla typowego poziomu dostępu do danych, właściwość Count jest często użyteczna, a ja wolałbym zwrócić ICollection < T> z właściwością Count lub ewentualnie IList < T> jeśli sądzę, że typowi konsumenci będą chcesz użyć indeksatora.

Wskazuje to również dzwoniącemu, że kolekcja została faktycznie zrealizowana. W ten sposób rozmówca może dokonać iteracji poprzez zwróconą kolekcję bez uzyskiwania wyjątków z poziomu dostępu do danych. To może być ważne. Na przykład usługa może generować strumień (np. SOAP) z zwróconego zbioru. Może to być kłopotliwe, jeśli wyjątek zostanie wyrzucony z warstwy dostępu do danych podczas generowania strumienia z powodu leniwej iteracji, ponieważ strumień wyjściowy jest już częściowo zapisany, gdy zgłoszony zostanie wyjątek.

0

Ponieważ metody przedłużania Linq zostały dodane do IEnumerable <T>, odkryłem, że moje wykorzystanie innych interfejsów znacznie się zmniejszyło; prawdopodobnie około 80%. Kiedyś stosowałem religijnie listę, ponieważ jej metody przyjmowały delegatów do leniwego oceniania, jak Find, FindAll, ForEach i tym podobne. Ponieważ jest to dostępne w rozszerzeniach System.Linq, zastąpiłem wszystkie te odniesienia odniesieniami do IEnumerable <T>.

0

Nie chciałbym iść z tablicą, jest to typ, który pozwala na modyfikację, ale nie ma dodawania/usuwania ... tak jak najgorsze opakowanie.Jeśli chcę zezwolić na modyfikacje, użyłbym typu, który obsługuje dodawanie/usuwanie.

Jeśli chcesz zapobiec modyfikacjom, już je zawijasz/kopiujesz, więc nie widzę, co jest nie tak z IEnumerable lub ReadOnlyCollection. Pójdę z tym później ... coś, czego nie lubię w IEnumerable, to to, że jest leniwy z natury, ale kiedy używasz ze wstępnie załadowanymi danymi tylko do owinięcia go, kod wywołujący, który z nim pracuje, ma tendencję do zakładania załadowane dane lub dodatkowe "niepotrzebne" linie :(... które mogą przybierać brzydkie wyniki podczas zmiany

Powiązane problemy