2010-07-05 12 views
5

Considerate questo codice:Capire l'estensione ElementAt (indice)

int size = 100 * 1000 * 1000; 
var emu = Enumerable.Range(0, size); 
var arr = Enumerable.Range(0, size).ToArray(); 

quando chiamo emu.ElementAt (dimensioni-10) e arr.ElementAt (dimensioni-10) e misurare il tempo del arr è molto più veloce (la matrice è 0,0002s rispetto a IEnumerable 0.59s).

quanto mi risulta, il metodo estensione ElementAt() hanno la firma

public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index) 

e dato che la 'sorgente' è un IEnumerable logica effettuata sarebbe simile - contrario di ciò che vedo dove la matrice è accessibile direttamente.

Qualcuno potrebbe spiegare questo :)

risposta

5

Questa è un'ottimizzazione eseguita a tempo di esecuzione. Sebbene la chiamata non sia sovraccaricata, è in grado di verificare (utilizzando is o as) se la fonte è in realtà un IList<T>. Se lo è, è in grado di andare direttamente all'elemento giusto.

Diverse altre chiamate fanno questo - notevole Count() che è ottimizzato per ICollection<T> e (a partire da. NET 4) l'interfaccia non generica ICollection.

Uno degli svantaggi dei metodi di estensione è che tutte queste ottimizzazioni devono essere eseguite dall'implementazione stessa - i tipi non possono sovrascrivere nulla per "optare" per l'ottimizzazione dei metodi di estensione. Ciò significa che le ottimizzazioni devono essere tutte conosciute dall'implementatore originale :(

+0

Puoi optare in qualche modo indirettamente per le ottimizzazioni avendo il tuo tipo di raccolta personalizzato implementare 'IList ', ma implementarlo esplicitamente e/o esporre la tua raccolta pubblicamente come 'IEnumerable '. Non è l'ideale, ma fondamentalmente questi sono i "ganci" di ottimizzazione. Se lo si desidera, è possibile fornire un'interfaccia personalizzata per ciascun metodo di estensione personalizzato che consenta a un autore della classe di sovrascrivere il comportamento del metodo di estensione, anche se potrebbe risultare un po 'ingombrante. (questo widget Widget) 'e' WidgetExtensions.IToggleWidget '. –

12

Calling ElementAt su un ciclo IEnumerable<T> volontà attraverso le voci fino a raggiungere l'indice desiderato. (Un'operazione O (n))

Calling ElementAt su un IList<T> (ad esempio una matrice) utilizzerà indicizzatore s' il IList<T> ottenere immediatamente l'indice desiderato. (Un'operazione O (1))

+0

Arh, quindi mi stai dicendo che ElementAt è sovraccarico per Array, Liste, Whatnot .. ?? – Moberg

+1

Non è sovraccarico. Utilizza un cast di tipo – SLaks