2009-07-31 8 views
14

Hi załóżmy te 2 metod:C# Lista <T> vs pytanie IEnumerable <T> wydajności

private List<IObjectProvider> GetProviderForType(Type type) 
     { 
      List<IObjectProvider> returnValue = new List<IObjectProvider>(); 

      foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders) 
      { 
       if ((provider.Key.IsAssignableFrom(type) || 
        type.IsAssignableFrom(provider.Key)) && 
        provider.Value.SupportsType(type)) 
       { 
        returnValue.Add(provider.Value); 
       } 
      } 
      return returnValue; 
     } 

private IEnumerable<IObjectProvider> GetProviderForType1(Type type) 
     { 
      foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders) 
       if ((provider.Key.IsAssignableFrom(type) || 
        type.IsAssignableFrom(provider.Key)) && 
        provider.Value.SupportsType(type)) 

        yield return provider.Value;    
     } 

który z nich jest szybszy? Kiedy patrzę na pierwszą metodę, widzę, że pamięć jest przydzielona dla List, co moim zdaniem nie jest potrzebne. Metoda IEnumerable wydaje mi się szybsza.

Na przykład, załóżmy, że nazywają

int a = GetProviderForType(myType).Count; 
int b = GetProviderForType1(myType).Count(); 

Teraz jest inny problem, czy istnieje różnica wydajności pomiędzy tymi 2 powyżej?

Co myślisz?

+13

Odpowiedź na wszystkie pytania "która jest szybsza?" pytania są takie same: Spróbuj w obie strony. Wyjdź ze stopera. Wtedy będziesz wiedzieć. –

Odpowiedz

30

W tym konkretnym przypadku, użycie formularza IEnumerable<T> będzie bardziej wydajne, ponieważ użytkownik musi znać tylko numer . Nie ma sensu przechowywanie danych, zmienianie rozmiaru buforów itp., Jeśli nie potrzebujesz.

Jeśli z jakiegoś powodu konieczne będzie ponowne użycie wyników, formularz List<T> będzie bardziej wydajny.

Należy zauważyć, że zarówno metoda Count() rozszerzenie i nieruchomość Count będzie skuteczny dla List<T> jako realizacja Count() sprawdza, czy sekwencja docelowa realizuje ICollection<T> i wykorzystuje właściwość Count jeśli tak.

Inną opcją, która powinna być nawet więcej wydajny (choć dopiero) byłoby wywołać przeciążenie Count która przyjmuje Delegat:

private int GetProviderCount(Type type) 
{ 
    return _objectProviders.Count(provider => 
     (provider.Key.IsAssignableFrom(type) 
     || type.IsAssignableFrom(provider.Key)) 
     && provider.Value.SupportsType(type)); 
} 

To będzie uniknąć dodatkowego poziomu indirections poniesionych przez Where i Select klauzule.

(Jak mówi Marc, dla małych ilości danych różnice wydajności prawdopodobnie będzie znikomy i tak.)

+0

Myślę, że powinieneś zwrócić int w tej sprawie. –

+0

Doh - oczywiście. Naprawiono, dziękuję :) –

+2

Wszystkie twoje odpowiedzi naprawdę mi pomogły, ale myślę, że ten ma wszystko wymienione, więc akceptuję to. Dziękuję Ci! –

4

Dokładna odpowiedź na takie pytania może się różnić w zależności od wielu czynników i może ulec zmianie wraz z ewolucją wskaźnika CLR. Jedynym sposobem, aby się upewnić, jest zmierzenie go - i pamiętaj, że jeśli różnica jest niewielka w porównaniu z operacją, w której się pojawi, powinieneś wybrać najbardziej czytelny, możliwy do utrzymania sposób pisania.

A w tej notatce, można również spróbować:

private IEnumerable<IObjectProvider> GetProviderForType1(Type type) 
{ 
    return _objectProviders.Where(provider => 
        provider.Key.IsAssignableFrom(type) || 
        type.IsAssignableFrom(provider.Key)) && 
        provider.Value.SupportsType(type)) 
          .Select(p => p.Value); 
} 

Można również dać sobie dużą elastyczność przy powrocie IEnumerable<T> a następnie za pomocą metody ToList przedłużacza, jeśli chcesz „Snapshot” wyniki w liście. Pozwoli to uniknąć powtórnej oceny kodu w celu wygenerowania listy, jeśli chcesz ją sprawdzić kilka razy.

4

Ważną częścią to pytanie brzmi „jak duży jest dane”? Ile wierszy ...

Dla małych ilości danych, lista jest w porządku - zajmie mało czasu, aby przydzielić wystarczająco dużą listę, i nie będzie zmieniać rozmiaru wiele razy (brak, jeśli można powiedzieć, jak duży być z góry).

Nie skaluje się to jednak do dużych wolumenów danych; wydaje się mało prawdopodobne, że twój dostawca obsługuje tysiące interfejsów, więc nie powiedziałbym, że jest konieczne, aby przejść do tego modelu - ale to nie zaszkodzi ogromnie.

Oczywiście, można użyć LINQ też:

return from provider in _objectProviders 
     where provider.Key.IsAssignableFrom(type) ... 
     select provider.Value; 

To także odroczony yield podejście pod kołdrą ...

2

Główna różnica między IEnumerable i IList:

IEnumerable: Implementuje polecenie MoveNext, Resetuj, pobierz bieżące metody i zwraca typ licznika Iterate Through Records.

IList: Odsłania interfejs IEnumerable, jak również jest zbiorem nietypowych obiektów, do których można uzyskać dostęp za pośrednictwem indeksu, więc IEnumerable + ICollection (Manipulation of data) i dodawanie, usuwanie, wstawianie (w określonym indeksie) są przydatne metody zaimplementowane przez IList.

Po przejrzeniu twojego kodu W mojej opinii IEnumerable jest bardziej efektywny, ale powracająca lista jest również przydatna, jeśli chcesz wykonać jakąś manipulację danymi i jeśli chcesz tylko iterować dane, to IEnumerable jest lepsze.

+1

Niepoprawnie. IEnumerable ma tylko jedną metodę. Zwracanie iteratora jest znacznie wydajniejsze niż tworzenie listy. – SLaks

+1

Inną możliwością byłoby powrócić typ, który implementuje 'ICollection' (nierodzajową!) W sposób tylko do odczytu, a także' ' IEnumerable i ewentualnie' ICollection '. Jeśli klasa implementująca 'IEnumerable ' również implementuje 'ICollection' lub' ICollection ', metoda rozszerzenia' Count' dla 'IEnumerable ' użyje metody 'Count' jednego z tych interfejsów. W przeciwnym razie będzie musiał wyliczyć wszystkie elementy, aby uzyskać liczbę. Zauważ, że forma nietypowa jest nieco bardziej przydatna do tego celu, ponieważ ... – supercat

+1

... 'IEnumeration ' może być użyte jako 'IEnumeration ', ale 'ICollection ' nie może być użyte jako 'ICollection '. Jeżeli procedura, która spodziewa się '' IEnumeration dano instancję klasy, która implementuje '' ICollection ale nie non-generic 'ICollection', to nie ma możliwości dowiedzenia się, że kolekcja powinna być oddane do' 'do kolekcji ICollection zdobyć liczyć. Jeśli klasa implementuje nierodzajową 'ICollection' jednak, że kod, który spodziewa się' 'IEnumerable nie miałby problemów ze znalezieniem tego. – supercat