2012-06-07 7 views
7

Odpowiedź na What is the expected performance of IEnumerable? mówi, że nie ma sposobu, aby dowiedzieć się czegoś o wykonaniu iteracji arbitralnego IEnumerable. Każda iteracja może trafić do bazy danych lub wykonać połączenie z serwisem sieciowym; lub może po prostu zwrócić następny element w tablicy/liście.Jaki jest dobry sposób, aby wskazać, że IEnumerable jest "wolny" lub "szybki"?

Biorąc to pod uwagę, czy istnieje dobry sposób na wskazanie "jest szybki"? Na przykład z T[] lub List<T> zamiast IEnumerable<T> Wiem, że przejście z T[i] do T[i+1] będzie szybkie. (Oczywiście, zmuszając wyliczenie zwraca listę/tablicy można utworzyć inne problemy z wydajnością. List<T> naraża również edytowalne semantykę.)

odwrotnie, byłoby powrocie IQueryable<T> zamiast IEnumerable<T> być dobrym sposobem, aby wskazać „to jest powolna”? A może IEnumerable<Task<T>>?

Klienci o numerze C nie mają możliwości dowiedzenia się, że modele IEnumerable<T> będą miały znacznie odmienne cechy wydajności.

class C 
{ 
    readonly T[] m_data; 
    public IEnumerable<T> ThisWillIterateQuickly { get { return m_data; } } 

    public IEnumeralbe<T> ThisWillIterateSlowly 
    { 
     get 
     { 
     T retval = ... an expensive database call ...; 
     yield return retval; 
     } 
    } 

    public IQueryable<T> IsThisBetterForSlow { get { return ThisWillIterateSlowly; } } 
    public T[] IsThisAGoodWayForFast { get { return m_data; } } 
} 
+4

Może to brzmieć mgliście, ale coś jest powolne * tylko * jeśli myślisz, że to nie * wystarczająco szybko *. –

+1

Nie ma standardowego mechanizmu reklamowania. Może po prostu: dokumentacja? –

+0

Re IQueryable ... Tablica może zostać ujawniona jako IQueryable - nie mówi nic o perf –

Odpowiedz

1

Po pomyśleniu o tym więcej, wydaje się, że problem/pytanie naprawdę koncentruje się na zachowaniu/działaniu IEnumerator.MoveNext(). Korzystanie Visual Studio 2012, udało mi się stworzyć wersje async z IEnumerator i IEnumerable:

public interface IAsyncEnumerator<T> : IDisposable 
{ 
    Task<T> CurrentAsync { get; } 
    Task<bool> MoveNextAsync(); 
    Task ResetAsync(); 
} 

public interface IAsyncEnumerable<T> 
{ 
    IAsyncEnumerator<T> GetAsyncEnumerator(); 
} 

Wadą tego podejścia jest to, że nie ma dużo wsparcia językowego; powyższe nie będzie działać z foreach. Ale metoda rozszerzenia może złagodzić ból:

public static class EnumeratorExtensions 
{ 
    public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) 
    { 
     using (var enumerator = enumerable.GetEnumerator()) 
     { 
      while (enumerator.MoveNext()) 
       action(enumerator.Current); 
     } 
    } 

    public static async Task ForEachAsync<T>(this IAsyncEnumerable<T> enumerable, Action<T> action) 
    { 
     using (var enumerator = enumerable.GetAsyncEnumerator()) 
     { 
      while (await enumerator.MoveNextAsync()) 
       action(await enumerator.CurrentAsync); 
     } 
    } 
} 
+0

Zastanawiasz się, dlaczego 'CurrentAsync' jest' Zadanie ', z pewnością praca jest wykonywana w' MoveNextAsync'? Więc możesz po prostu mieć 'T Current {get; } ' – Lukazoid

+0

@Lukazoid: Myślę, że od samego wdrożenia zależy decyzja, gdzie wykonać rzeczywistą pracę; MoveNextAsync() może po prostu zwiększyć indeks. CurrentAsync może przejść przez sieć do zdalnego serwera. –

+1

Niewystarczająco uczciwe, nie wydaje mi się to intuicyjne, ponieważ oczekiwałbym, że zdobywca nieruchomości będzie szybki. I dlaczego konsument przeprowadzi się dalej, jeśli nie będzie chciał kolejnego przedmiotu? Moim zdaniem bardziej sensowne jest, aby 'MoveNextAsync' zawsze wykonywał pracę. – Lukazoid

3

Odwrotnie, to powrót IQueryable zamiast IEnumerable być dobrym sposobem, aby wskazać "to jest powolna"?

Nie, IQueryable<T> nieodłączne od IEnumerable<T> ... i problem z IEnumerable<T>T jest to, że może to być IQueryable<T> z ogromnym boczny wpływa podczas iteracji, jak zapytania zdalnej bazy danych lub coś podobnego.

Zabawne, prawda?

Prawdopodobnie powinieneś zapytać o IEnumerable<T> V.S. List<T>, z których drugi zdecydowanie zawiera dane i nie musi pobierać go z innego miejsca.

+0

Może 'IQueryable ' lepiej zasugerować "to może trafić do bazy danych" niż 'IEnumerable '? Czy jest to dodanie semantyki, której tak naprawdę tam nie ma? –

+1

@Dan. 'IQueryable' zwykle trafia do bazy danych, ale nie musi, może uzyskać jej wartość z innego miejsca lub trzymać ją w środku. Ale tak "IQueryable " oznacza, że ​​jest coś, efekt uboczny lepszy od 'IEnumerable '. – gdoron

2

Jeśli chcesz zagwarantować, że ta iteracja nie będzie zawierała żadnych niespodzianek, to zgadzam się, wystawię T[] - moduł wyliczający nie może zostać nadpisany, ponieważ nie możesz dziedziczyć z tablic. Iteracja jest również efektem ubocznym, tego samego nie można powiedzieć o IEnumerable<T>.

Chciałbym jednak zgadzam się, że odsłaniając tablicę wyraża tę wiadomość, co jest ważniejsze (moim zdaniem). Wydajność czegoś nigdy nie może być wyrażona w kodzie, chyba że zaczniesz nazywać rzeczy CheapIteration lub ExpensiveIteration.

Po drugiej stronie monety, z tablicą, po prostu przesuwasz problem z wydajnością iteracji na żądanie do momentu zapełnienia tablicy. To jest gwarantowane, aby w pełni rozwiązać problem z wydajnością, ponieważ będzie to pełna iteracja zawartości dostarczającej zawartość tablicy. W przypadku IEnumerable<T>, jeśli iteracja zostanie zatrzymana, występuje problem z wydajnością - najszybszy kod to kod, który nie działa.

+0

Wymuszenie wyliczenia na 'T []' pozwala mi wnioskować więcej niż 'IEnumerable '. –

+1

@Dan Wymuszenie tego na 'T []' wiąże się z pełną iteracją, która może być droga. Z 'T []' tracisz także odroczoną realizację i wymuszasz przebieg całej wyliczalnej. Zwracając 'T []' zamiast 'IEnumerable ' wyraża ** inną intencję **, ludzie będą mieli różne oczekiwania. –

+0

Tak, 'IArrayLikeEnumerable ' powiedziałoby wyraźnie "jest to' IEnumerable 'z' T [] '-like performance". Czy istnieje lepszy sposób, aby powiedzieć, że przy użyciu standardowych funkcji? Powinien/powinien/może Microsoft dodać 'IArrayLikeEnumerable ' do BCL? –

1

Zwykle używam właściwości do zwracania "szybkich" wyliczeń i metod "powolnego".

Twój problem jest argumentem za używanie asynchronicznego podejścia i projektowania w jak największym stopniu.Wtedy naprawdę nie ma znaczenia, jak długo trwa wyliczanie, ponieważ interfejs jest responsywny, a użytkownik szczęśliwy ;-)

Powiązane problemy