Właśnie przeczytałem interesujący artykuł zatytułowany Robi się zbyt słodki z zyskiem c #
Zastanawiałem się, jaki jest najlepszy sposób na wykrycie, czy IEnumerable jest faktyczną kolekcją, którą można wyliczyć, czy też jest maszyną stanu wygenerowaną za pomocą słowa kluczowego fed.
Na przykład możesz zmodyfikować DoubleXValue (z artykułu) na coś takiego:
private void DoubleXValue(IEnumerable<Point> points)
{
if(points is List<Point>)
foreach (var point in points)
point.X *= 2;
else throw YouCantDoThatException();
}
Pytanie 1) Czy jest na to lepszy sposób?
Pytanie 2) Czy powinienem się tym martwić podczas tworzenia interfejsu API?
c#
api-design
ConditionRacer
źródło
źródło
ICollection<T>
byłby lepszym wyborem, ponieważ nie wszystkie kolekcje sąList<T>
. Na przykład tablicePoint[]
implementują,IList<T>
ale nie sąList<T>
.IEnumerable
jest niezmienny w tym sensie, że nie można modyfikować kolekcji (dodawać / usuwać) podczas jej wyliczania, jednak jeśli obiekty na liście można modyfikować, to całkiem rozsądne jest zmodyfikowanie ich podczas wyliczania listy.ToList
? msdn.microsoft.com/en-us/library/bb342261.aspx Nie wykrywa, czy został wygenerowany przez wydajność, ale czyni go nieistotnym.Odpowiedzi:
Twoje pytanie, jak rozumiem, wydaje się opierać na niewłaściwym założeniu. Zobaczmy, czy mogę zrekonstruować rozumowanie:
Problem polega na tym, że druga przesłanka jest fałszywa. Nawet gdybyś mógł wykryć, czy dany IEnumerable był wynikiem transformacji bloku iteratora (i tak, istnieją sposoby, aby to zrobić), nie pomogłoby to, ponieważ założenie jest błędne. Zilustrujmy dlaczego.
W porządku, mamy cztery metody. S1 i S2 są automatycznie generowanymi sekwencjami; S3 i S4 są sekwencjami generowanymi ręcznie. Załóżmy teraz, że mamy:
Wynik dla S1 i S4 wyniesie 0; za każdym razem, gdy wyliczasz sekwencję, dostajesz nowe odniesienie do utworzonego M. Wynik dla S2 i S3 wyniesie 100; za każdym razem, gdy wyliczasz sekwencję, dostajesz to samo odniesienie do M, które dostałeś ostatni raz. To, czy kod sekwencji jest generowany automatycznie, czy nie, jest prostopadłe do pytania, czy wyliczone obiekty mają tożsamość referencyjną, czy nie. Te dwie właściwości - automatyczne generowanie i tożsamość referencyjna - w rzeczywistości nie mają ze sobą nic wspólnego. Artykuł, który podlinkowałeś, nieco je łączy.
O ile dostawca sekwencji nie jest udokumentowany jako zawsze oferujący obiekty, które mają tożsamość referencyjną , nie należy zakładać, że tak jest.
źródło
Myślę, że kluczową koncepcją jest to, że każdą
IEnumerable<T>
kolekcję należy traktować jako niezmienną : nie należy modyfikować z niej obiektów, należy utworzyć nową kolekcję z nowymi obiektami:(Lub napisz to bardziej zwięźle, używając LINQ
Select()
.)Jeśli naprawdę chcesz zmodyfikować elementy w kolekcji, nie używaj
IEnumerable<T>
, nie używajIList<T>
(lub ewentualnieIReadOnlyList<T>
jeśli nie chcesz modyfikować samej kolekcji, tylko elementy w niej, a jesteś w .Net 4.5):W ten sposób, jeśli ktoś spróbuje użyć twojej metody
IEnumerable<T>
, zawiedzie ona podczas kompilacji.źródło
IEnumerable
wielokrotnego powtarzania . Potrzeba zrobienia tego więcej niż jeden raz można uniknąć, wywołującToList()
i iterując listę, chociaż w konkretnym przypadku pokazanym przez PO wolę rozwiązanie svicka polegające na tym, że DoubleXValue również zwraca IEnumerable. Oczywiście w prawdziwym kodzie prawdopodobnieLINQ
użyłbym tego do wygenerowania tego typuIEnumerable
. Jednak wybór Svickayield
ma sens w kontekście pytania PO.Select()
. A jeśli chodzi o wielokrotne iteracjeIEnumerable
: ReSharper jest w tym pomocny, ponieważ może cię ostrzec, kiedy to zrobisz.Osobiście uważam, że problem wskazany w tym artykule wynika z nadmiernego używania
IEnumerable
interfejsu przez wielu programistów C # w dzisiejszych czasach. Wygląda na to, że stał się domyślnym typem, gdy potrzebujesz kolekcji obiektów - ale jest wiele informacji, któreIEnumerable
po prostu nie mówią ci ... co najważniejsze: czy zostało to potwierdzone czy nie *.Jeśli okaże się, że musisz wykryć, czy an
IEnumerable
jest naprawdę an,IEnumerable
czy czymś innym, abstrakcja wyciekła i prawdopodobnie jesteś w sytuacji, gdy typ parametru jest nieco zbyt luźny. Jeśli potrzebujesz dodatkowych informacji, którychIEnumerable
sam nie zapewnia, wyraź to wymaganie, wybierając jeden z innych interfejsów, takich jakICollection
lubIList
.Edytuj dla downvoters Wróć i przeczytaj uważnie pytanie. OP prosi o sposób zagwarantowania, że
IEnumerable
instancje zostały zmaterializowane przed przekazaniem ich do jego metody API. Moja odpowiedź dotyczy tego pytania. Istnieje szerszy problem dotyczący właściwego sposobu użyciaIEnumerable
, a na pewno oczekiwania dotyczące kodu, który wklejono OP, są oparte na nieporozumieniu tego szerszego problemu, ale OP nie zapytał, jak prawidłowo używaćIEnumerable
lub jak pracować z niezmiennymi typami . Nie jest sprawiedliwe głosować za to, że nie odpowiedziałem na pytanie, które nie zostało postawione.* Używam tego terminu
reify
, inni używająstabilize
lubmaterialize
. Nie sądzę, aby społeczność przyjęła jeszcze wspólną konwencję.źródło
ICollection
, aIList
jest to, że są one zmienne (lub przynajmniej patrzeć w ten sposób).IReadOnlyCollection
iIReadOnlyList
.Net 4.5 rozwiązać niektóre z tych problemów.