2009-04-29 25 views
17

Mam tablica ListViewItems (ListViewItem[]), gdzie przechowuję obiekt SalesOrderMaster w każdym ListViewItem.Tag do późniejszego odniesienia.Jak przekonwertować typ anonimowy na silny typ w LINQ?

Mam teraz kod, który przechodzi przez każdy ListViewItem bezpiecznie rzuca właściwość .Tag do obiektu SalesOrderMaster, a następnie dodaje ten obiekt do kolekcji SalesOrders, dopiero po sprawdzeniu, aby upewnić się, że zamówienie jeszcze nie istnieje w tym zbiorze.

Proces do porównać zamówień sprzedaży jest kosztowne, a ja chciałbym przekonwertować to do wyrażenia LINQ dla przejrzystości i wydajności. (Mam również zainstalowany Parallel Extensions to .NET Framework 3.5, więc mogę go użyć, aby jeszcze bardziej poprawić wydajność LINQ).

Więc bez dalszych ceregieli: To jest to, co mam, a następnie to, czego chcę. (Co chcę nie będzie kompilować, więc wiem, że robię coś złego, ale mam nadzieję, że ilustruje punkt)

Co mam: (powolny)

foreach (ListViewItem item in e.Argument as ListViewItem[]) 
      { 
       SalesOrderMaster order = item.Tag as SalesOrderMaster; 
       if (order == null) 
       { 
        return; 
       } 
       if (!All_SalesOrders.Contains(order)) 
       { 
        All_SalesOrders.Add(order); 
       } 
      } 

Czego chcę: (teoria)

List<SalesOrderMaster> orders = 
(from item in (e.Argument as ListViewItem[]).AsParallel() 
select new { ((SalesOrderMaster)item.Tag) }).Distinct(); 

EDIT: wiem, że obsada jest tani, powiedziałem „Porównaj”, która w tym przypadku przekłada się na .Contains (kolejności) operacja

EDIT: odpowiedź każdego człowieka było niesamowite! Chciałbym zaznaczyć więcej niż jedną odpowiedź, ale ostatecznie muszę wybrać jedną.

EDIT: To jest to, co skończyło się z:

List<SalesOrderMaster> orders = 
(from item in (e.Argument as ListViewItem[]) select (SalesOrderMaster) item.Tag).GroupBy(item => item.Number).Select(x => x.First()).ToList(); 

Odpowiedz

17

widzę nikt nie odniósł swoje potrzeby konwertowania typ anonimowy do określonego typu jawnie, tak tu idzie ... używając „select new { }” tworzysz typ anonimowy, ale nie musisz. Można napisać zapytanie tak:

List<SalesOrderMaster> orders = 
    (from item in (e.Argument as ListViewItem[]).AsParallel() 
    select (SalesOrderMaster)item.Tag) 
    .Distinct() 
    .ToList(); 

Zauważ, że kwerenda wybiera (SalesOrderMaster)item.Tag bez new { }, więc nie tworzy typ anonimowy. Zauważ też, że dodałem ToList(), ponieważ chcesz List<SalesOrderMaster>.

Rozwiązuje to problem z anonimowym typem.Zgadzam się jednak z Markiem i Guffą, że użycie zapytania równoległego nie jest najlepszą opcją. Aby korzystać HashSet<SalesOrderMaster> jak sugeruje Guffa, można to zrobić:

IEnumerable<SalesOrderMaster> query = 
    from item in (ListViewItem[])e.Argument 
    select (SalesOrderMaster)item.Tag; 

HashSet<SalesOrderMaster> orders = new HashSet<SalesOrderMaster>(query); 

(I unikać korzystania var więc zwracane typy są jasne w przykładach).

+1

+1 dla informacji o 'nowym {}' –

+0

@ [Lucas] Ale jak możemy wybrać wiele pól za pomocą tej metody? Czy jest jakaś alternatywa dla używania anonimowego typu? – Zesty

+1

Ach, nieważne, rozumiem. wybierz nową osobę (e.Nazwa, e.Last_name)). ToList () – Zesty

3

części, w tym kodzie, który jest kosztowne jest wywołanie metody Contains na liście. Ponieważ jest to operacja O (n), tym wolniej, im więcej obiektów dodasz do listy.

Po prostu użyj obiektu HashSet<SalesOrderMaster> dla obiektów zamiast List<SalesOrderMaster>. Metoda Contains jest operacją O (1), więc twoja pętla będzie operacją O (n) zamiast operacji O (n * n).

+1

Ale dodawania elementów do zestawu hash jest O (n) operacja co O (log n) dodaje, więc otrzymujesz O (n * log n) – configurator

+0

Tak, dodawanie elementów do zestawu mieszającego jest czasem operacją O (n), ale tak samo jest dodawanie elementów do listy, więc względny wynik jest prawie taki sam. – Guffa

+0

Aby było jasne, dodanie elementu zajmuje mniej więcej tyle samo czasu, aby określić, czy zawiera on już element, który jest znacznie szybszy w programie HashSet. Czy to to? – Lucas

3

Podobnie jak Marc Gravell powiedział, że nie powinno dostępu do właściwości z różnych wątków Tag i obsada jest dość tani, więc masz:

var items = (e.Argument as ListViewItem[]).Select(x=>x.Tag) 
     .OfType<SalesOrderMaster>().ToList(); 

ale potem, chcesz znaleźć różne przedmioty - tutaj można spróbować użyć AsParallel:

var orders = items.AsParallel().Distinct(); 
Powiązane problemy