2009-04-29 13 views
17

Ho un array di ListViewItems (ListViewItem[]), dove memorizzo un oggetto SalesOrderMaster in ogni ListViewItem.Tag per un riferimento futuro.Come posso convertire il tipo anonimo in tipo forte in LINQ?

Ho un codice che in questo momento, passa attraverso ogni ListViewItem cas in modo sicuro la proprietà .Tag in un oggetto SalesOrderMaster, quindi aggiunge quell'oggetto a una raccolta di SalesOrders, solo dopo aver verificato che l'ordine non esista già in quella raccolta.

Il processo per confronta gli ordini di vendita è costoso e vorrei convertirlo in un'espressione LINQ per chiarezza e prestazioni. (Ho anche installato il Parallel Extensions to .NET Framework 3.5 per migliorare ulteriormente le prestazioni LINQ)

Quindi, senza ulteriori indugi: questo è quello che ho, e poi quello che voglio. (Quello che voglio non si compila, quindi so che sto facendo qualcosa di sbagliato, ma spero che illustra il punto)

Quello che ho: (Slow)

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); 
       } 
      } 

Quello che voglio: (Teoria)

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

EDIT: so che il cast è a buon mercato, ho detto la "Confronta", che in questo caso si traduce ai .Contains (ordine) funzionamento

EDIT: la risposta di tutti è stato fantastico! Vorrei poter segnare più di una risposta, ma alla fine devo sceglierne una.

EDIT: Questo è quello che ho finito con:

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

risposta

17

vedo nessuno ha affrontato il vostro bisogno di convertire un tipo anonimo a un tipo di nome in modo esplicito, quindi ecco qui ... utilizzando "select new { }" si sta creando un tipo anonimo, ma non è necessario. È possibile scrivere la query in questo modo:

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

Notare che la query seleziona (SalesOrderMaster)item.Tag senza new { }, in modo da non creare un tipo anonimo. Si noti inoltre che ho aggiunto ToList() poiché si desidera un List<SalesOrderMaster>.

Questo risolve il problema del tipo anonimo.Tuttavia, sono d'accordo con Mark e Guffa che usare una query parallela qui non è la migliore opzione. Per utilizzare HashSet<SalesOrderMaster> come suggerito Guffa, si può fare questo:

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

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

(ho evitato di usare var in modo che i tipi restituiti sono chiare negli esempi.)

+1

+1 per le informazioni su 'new {}' –

+0

@ [Lucas] Ma come possiamo selezionare più campi usando questo metodo? C'è qualche alternativa all'utilizzo di un tipo anonimo? – Zesty

+1

Ah, non importa, capito. seleziona nuova Persona (e.First_name, e.Last_name)) .Lista () – Zesty

3

La parte in quel codice che è costoso sta chiamando il metodo Contains sulla lista. Essendo un'operazione O (n), diventa più lento più oggetti aggiungi alla lista.

Basta usare uno HashSet<SalesOrderMaster> per gli oggetti anziché uno List<SalesOrderMaster>. Il metodo Contains di HashSet è un'operazione O (1), pertanto il ciclo sarà un'operazione di tipo O (n) anziché un'operazione di tipo O (n * n).

+1

Ma l'aggiunta di elementi a un insieme di hash è un O (n) operazione ogni O (log n) aggiunge, quindi ottieni O (n * log n) – configurator

+0

Sì, l'aggiunta di elementi a un set di hash è un'operazione O (n) a volte, ma così è l'aggiunta di elementi a un elenco, quindi il relativo il risultato è più o meno lo stesso – Guffa

+0

Quindi, per essere chiari, aggiungere l'elemento richiede circa lo stesso tempo per entrambi, è determinare se contiene già l'elemento che è molto più veloce in HashSet. È così? – Lucas

3

come Marc Gravell ha detto, non si deve accedere alla proprietà Tag da diversi thread, e il cast è abbastanza a buon mercato, in modo da avere:

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

ma poi, si vuole trovare elementi distinti - qui si può provare a utilizzare AsParallel:

var orders = items.AsParallel().Distinct(); 
Problemi correlati