2013-06-24 14 views
29

podawany był zainicjowany IEnumerable:Jak mogę skutecznie określić, czy IEnumerable ma więcej niż jeden element?

IEnumerable<T> enumerable; 

chciałbym ustalić, czy ma więcej niż jeden element. Myślę, że najbardziej oczywistym sposobem na to jest:

enumerable.Count() > 1 

Uważam jednak, Count() wylicza kolekcję cały, co jest konieczne w tym przypadku zastosowania. Na przykład, jeśli kolekcja zawiera bardzo dużą liczbę elementów lub dostarcza jej dane z zewnętrznego źródła, może to być dość nieekonomiczne pod względem wydajności.

Jak mogę to zrobić bez wyliczania więcej niż 2 elementów?

+0

Jeśli musisz na to liczyć, to zrób coś, lepiej byłoby zmusić go do oceny na liście lub innej kolekcji. – devshorts

+0

@devshorts, by * count on it *, masz na myśli * zależy od tego działa * lub * używa 'Count()' method *? – Sam

+1

Chodzi mi o to, że jeśli chcesz dowiedzieć się, ile elementów na nim jest, to zrób coś w zależności od tego, możesz równie dobrze to ocenić. 'Count' oceni ją raz, a następnie, jeśli ją powtórzysz, musisz wszystko jeszcze raz ocenić. To samo z każdą inną metodą 'IEnumerable', taką jak' Take' lub 'Skip'. To naprawdę zależy od danych. – devshorts

Odpowiedz

35

Można to sprawdzić na wiele sposobów, łącząc metody rozszerzenie w System.Linq ... Dwa proste przykłady poniżej:

bool twoOrMore = enumerable.Skip(1).Any(); 
bool twoOrMoreOther = enumerable.Take(2).Count() == 2; 

Wolę pierwszego od wspólnej drodze, by sprawdzić, czy jest Count() >= 1 z Any() i dlatego uważam, że jest bardziej czytelny.

+3

To nie zawsze jest poprawne, jeśli jest to strumień danych, w których utraciłeś dane pomijane i/lub brane. Również jeśli dane zmieniają się za każdym razem, gdy wykonujesz operację przeliczania, oznacza to, że twoja ocena danych może być inna po wykonaniu tego testu. – devshorts

+0

@devshorts, ale martwi go tylko liczba, a nie rzeczywiste dane. –

+0

@devshorts Prawda. Oczywiście w razie potrzeby można zapisać wynik Take (2) w zmiennej, ale nie było to wymagane w pytaniu. –

4

Dla zabawy, zadzwoń do Next() dwa razy, a następnie zdobądź kolejne IEnumerable.

Lub napisz małą klasę opakowania dla tego konkretnego celu: EnumerablePrefetcher : IEnumerable<T>, aby spróbować pobrać określoną liczbę elementów podczas inicjowania.

Jego IEnumerable<T> GetItems() metoda powinna używać powrotu plastyczności w ten sposób

foreach (T item in prefetchedItems) // array of T, prefetched and decided if IEnumerable has at least n elements 
{ 
    yield return item; 
} 
foreach (T item in otherItems) // IEnumerable<T> 
{ 
    yield return item; 
} 
+0

Dzięki; Nie myślałem o zrobieniu wstępnego pobierania lub buforowania 'IEnumerable'! – Sam

+2

Próbka kodu może być wyrażona jako 'return prefetchedItems.Concat (otherItems);' chociaż powyższe jest również stałe. –

+0

@CameronS, dopiero co zdałem sobie sprawę, że 'Concat' istnieje; dzięki! Myślałem, że takiej funkcjonalności brakuje w ramach. – Sam

0

@ roztwór Cameron-S jest prostszy, lecz poniżej jest bardziej efektywne. Wymyśliłem to na podstawie metody Enumerable.Count(). Skip() będzie zawsze iterować, a nie powodować zwarcia, aby uzyskać source liczbę dla typu ICollection lub ICollection<T>.

/// <summary> 
/// Returns true if source has at least <paramref name="count"/> elements efficiently. 
/// </summary> 
/// <remarks>Based on int Enumerable.Count() method.</remarks> 
public static bool HasCountOfAtLeast<TSource>(this IEnumerable<TSource> source, int count) 
{ 
    source.ThrowIfArgumentNull("source"); 
    var collection = source as ICollection<TSource>; 
    if (collection != null) 
    { 
     return collection.Count >= count; 
    } 
    var collection2 = source as ICollection; 
    if (collection2 != null) 
    { 
     return collection2.Count >= count; 
    } 
    int num = 0; 
    checked 
    { 
     using (var enumerator = source.GetEnumerator()) 
     { 
      while (enumerator.MoveNext()) 
      { 
       num++; 
       if (num >= count) 
       { 
        return true; 
       } 
      } 
     } 
    } 
    return false; // < count 
} 
Powiązane problemy