2010-02-03 1 views
8

Mam na myśli re-factor, ale nie wiem, czy efekt końcowy jest po prostu przesadzony. Obecnie mamUżywa linq w tej sytuacji overkill

IList<myobjecttypebase> m_items; 

    public int GetIncrementTotal() 
    { 
    int incrementTot; 
    foreach(myobjecttypebase x in m_items) 
    { incrementTot += x.GetIncrement(); } 
    } 

byłoby przesadą i/lub mniej efektywne w użyciu LINQ do ForEach

m_items.ToList().ForEach(x => incrementTot += x.GetIncrement()); 

Czy obsada być znaczny narzut tutaj?

Odpowiedz

4

Sposób ToList jest metoda rozszerzenie stosowania z LINQ, ale metoda ForEach jest tylko metoda w klasie Lista.

Najważniejszym narzutem tutaj jest wywołanie metody ToList, która tworzy nową listę z kolekcji. ForEach ma również niewielki narzut, ponieważ musi wywołać delegata dla każdej pozycji.

Jeśli chcesz użyć metody LINQ, sposób sumaryczny wydaje się bardziej odpowiedni:

public int GetIncrementTotal() { 
    return m_items.Aggregate(0, (t, i) => t + i.GetIncrement()); 
} 

lub suma:

public int GetIncrementTotal() { 
    return m_items.Sum(i => i.GetIncrement()); 
} 

Albo ma niewielki narzut nad swoim pierwotnym brzmieniu, więc jeśli chcesz najbardziej wydajny, po prostu trzymaj się prostej pętli.

+0

Dzięki za wyczerpującą odpowiedź Guffa. – Andrew

+0

"Nieznaczny narzut" tutaj jest tylko różnicą między wywołaniem delegata i wbudowanym kodem. Dopóki kolekcja nie jest ogromna lub podsumowujesz wiele kolekcji wykorzystujących to jako podstawę do wyboru kodowania, wchodzi w obszar przedwczesnej optymalizacji. Wprowadzaj zmiany w ten sposób, tylko jeśli testy wydajności pokażą, że uzyskasz znaczną poprawę całkowitego czasu pracy. – Richard

3

Narzutu będzie w iteracji kolekcji dwukrotnie.

m_items.ToList() // First iteration to add to the list all the items 
    .ForEach(x => incrementTot += x.GetIncrement()); /* Second iteration to 
                 perform logic */ 

ToList nie wykonuje iteracje w sposób jak najbardziej leniwym sprawozdania LINQ nich będzie zmusić powtórzyć, kod nad kolekcji dwukrotnie.

Generalnie formularz LINQ wydaje się być czytelniejszy, ale jeśli martwisz się wydajnością, lepiej go unikaj.

+0

Zakładam, że wersja linq byłaby jedyna z obciążeniem. Jeśli tak, to dlaczego metoda ToList() musi iterować nad wyliczeniem, aby rzucić ją z ILISTa – Andrew

+0

@Andrew - ponieważ musi przekonwertować kolekcję na listę . – Oded

+0

@Andrew: Metoda ToList() nie jest rzutowaniem. Jest to metoda konwersji i przydziela i wypełnia nowy obiekt List <>. –

7

Dlaczego nie używać operatora SUM bezpośrednio na IList?

To ma kilka przeciążeń za pomocą Func delegatów:

m_items.Sum(x => x.GetIncrement()); 
+0

Metoda GetIncrement jest metodą abstrakcyjną w klasie bazowej, a różne implementacje betonu zastępują wartość przyrostu. W przeciwnym razie po prostu użyłbym .Count() na IList – Andrew

+0

Nie zmienia odpowiedzi ... – Oded

+0

Widzę twój punkt dzięki poniższemu przykładowi kodu Johna. – Andrew

1

nie rób tego w ten sposób.
Tutaj ToList() przydzieli i zapełni nową listę, IMHO nie jest konieczne. Mimo że jest to trywialne ćwiczenie polegające na napisaniu metody rozszerzenia ForEach do iterowania nad dowolnym obiektem, który implementuje IEnumberable, odradzam (patrz artykuł Eric Lipperta foreach V's ForEach).

Poszedłbym z foreach.

P.S. trzeba zainicjować incrementTot (przepraszam, nie mogłem się powstrzymać)

2

Jak wspomniano Oded use SUM

incrementTot = m_items.ToList().Sum(x => x.GetIncrement()); 
+2

Po co pisać listę, jeśli wszystko, co zamierzasz zrobić, to suma wartości? –

+0

Dzięki za przykład. Nie rozumiem, co Oded miał na myśli, ponieważ jestem stosunkowo nowy w Linq. – Andrew

+0

Jon skeet ma rację raz. ToList jest zbędne. –

3

ForEach nie jest częścią LINQ - to część List<T> API i jest od tego czasu. NET 2.0.

Powodem, dla którego nie jest częścią LINQ jest to, że jest to metoda naturalnie uboczna ... a LINQ nie zachęca do działań niepożądanych.

Korzystanie Sum jest dobrym rozwiązaniem tutaj, ale nie trzeba utworzyć listę pierwsze:

return m_items.Sum(x => x.GetIncrement());