2011-01-07 9 views
7

Po otrzymaniu d możesz zajmować się ustaloną sekwencją, taką jak lista lub tablica, AST, która wyliczy niektóre zewnętrzne źródła danych, a nawet AST w niektórych istniejących kolekcjach. Czy istnieje sposób na bezpieczne "zmaterializowanie" rachunku przeliczalnego, aby operacje wyliczania takie jak foreach, liczyć itp. Nie wykonywały za każdym razem AST?Czy istnieje sposób na zapamiętanie lub zmaterializowanie IEnumerable?

Często używam .ToArray() do tworzenia tej reprezentacji, ale jeśli podstawową pamięcią jest już lista lub inna ustalona sekwencja, która wydaje się zmarnowanym kopiowaniem. Byłoby miło, gdybym mógł zrobić

var enumerable = someEnumerable.Materialize(); 

if(enumberable.Any() { 
    foreach(var item in enumerable) { 
    ... 
    } 
} else { 
    ... 
} 

bez obaw, że .Any() i foreach próbować wyliczyć sekwencję dwukrotnie i bez unccessarily kopiowanie przeliczalny.

+1

To jest fajny pomysł, ale chciałbym zwrócić uwagę, że obecnie istniejąca biblioteka Collective.ToList ma na celu ochronę przed mutacjami w istniejącej kolekcji. – Ani

+0

Problem z .ToList() polega na tym, że utworzy listę wyliczeń, które nie są listami (tablice, ICollections itp.) I zwrócą zmienną kolekcję. –

Odpowiedz

6

dość łatwe:

public static IList<TSource> Materialize<TSource>(this IEnumerable<TSource> source) 
{ 
    if (source is IList<TSource>) 
    { 
     // Already a list, use it as is 
     return (IList<TSource>)source; 
    } 
    else 
    { 
     // Not a list, materialize it to a list 
     return source.ToList(); 
    } 
} 
+3

Jest to dobre podejście. Myślę, że lepiej byłoby zamiast tego zwrócić 'IEnumerable ', a także sprawdzić 'ICollection' i' ICollection '. – Ani

+4

Jest to subtelnie odmienne od implementacji Linq.ToList(), która wydaje się zawsze zwracać nową kopię, więc zmiany w wyniku nie zmieniają oryginału. Materializuj zgodnie z pisemną wolą, w zależności od typu danych wejściowych, czasami zwracaj kopię, a czasami zwracaj oryginał - więc zmiany w wyniku czasem zmieniają oryginał. – Handcraftsman

+0

Ani ma dobry pomysł. Moim zamiarem nie jest stworzenie listy, która może być zmienna, po prostu 'IEnumerable ', która jest bezpieczna i wydajna do wielokrotnego wyliczenia. Ponadto, chociaż nigdy go nie testowałem, zakładam, że ToArray() jest tańszym materiałem zastępczym. –

2

Sprawdź ten wpis na blogu pisałem kilka lat temu: http://www.fallingcanbedeadly.com/posts/crazy-extention-methods-tolazylist/

w nim zdefiniować metodę zwaną ToLazyList które skutecznie robi to, czego szukasz.

Jak napisano, ostatecznie utworzy pełną kopię sekwencji wejściowej, chociaż można ją zmodyfikować tak, aby instancje IList nie zostały zawinięte w LazyList, co uniemożliwiłoby to zajście (to działanie, będzie nosił przy sobie założenie, że każdy IList, który dostajesz, jest już skutecznie zapamiętywany).

+1

To naprawdę interesujące rozszerzenie, ale nie sądzę, że jest to związane z tym, co chce OP. To * odradza * materializację sekwencji na podstawie potrzeb, podczas gdy OP chce * ochoczo * zmaterializować sekwencję w efektywny sposób; uzyskanie odniesienia do istniejącej kolekcji, jeśli zajdzie taka potrzeba. – Ani

+0

Wygląda na to, że coś jest zepsute na Twoim blogu. Adres URL, który był w tym wpisie, pozbawiony "www" przekierowany do strony głównej –

6

samo jak odpowiedź Thomasa, tylko nieco lepiej według mnie:

public static ICollection<T> Materialize<T>(this IEnumerable<T> source) 
{ 
    if (source == null) 
     return null; 

    return source as ICollection<T> ?? source.ToList(); 
} 

Należy pamiętać, że mają tendencję do powrotu istniejący samą kolekcję jeśli jego ważność zbiórki wpisz, lub w inny sposób wytworzy nową kolekcję. Podczas gdy te dwie są subtelnie różne, nie sądzę, że może to być problem.

Powiązane problemy