Domanda

Qualche tempo fa ho scritto un IList Metodo di estensione per elencare attraverso una parte di un elenco utilizzando gli indici. Mentre refactoring ho capito che una query simile poteva essere eseguita chiamando Skip(toSkip).Take(amount). Durante il benchmarking l'ho notato Skip non è ottimizzato per IList. Con un po 'di googling sono finito in un post di Jon Skeet, discutere perché ottimizzare i metodi come Skip è pericoloso.

Per quanto ho compreso l'articolo, il problema non viene lanciato eccezione nei metodi ottimizzati quando la raccolta viene modificata, ma come afferma un commento, la documentazione di MSDN si confonde.

In Ienumerator.MoveNext ():

Se vengono apportate modifiche alla raccolta, come l'aggiunta, la modifica o l'eliminazione degli elementi, l'enumeratore è irremovibilmente invalidato e la chiamata successiva a MovEnext o reset lancia un Exception invalidoperation.

In Ienumerator.getEnumerator ():

Se vengono apportate modifiche alla raccolta, come l'aggiunta, la modifica o l'eliminazione degli elementi, l'enumeratore è irremovibilmente invalidato e il suo comportamento è indefinito.

Vedo il merito in entrambe le convenzioni e sono un po 'perso se ottimizzare o meno. Cos'è una soluzione corretta? Ho preso in considerazione un IList.AssumeImmutable() approccio lungo le linee di AsParallel() Come menzionato da Kris Vandermotten nei commenti. Qualche implementazione esiste già o è una cattiva idea?

È stato utile?

Soluzione

Sono d'accordo con Rafe che il comportamento indefinito sia più corretto. Solo le raccolte in versione possono lanciare eccezioni e non tutte le raccolte sono in versione (gli array sono l'esempio più grande). Anche le raccolte in versione versione potrebbero comportarsi male se si apportano esattamente 2^32 modifiche tra le chiamate a MoveNext.

Supponendo che ti preoccupi davvero del comportamento di versioni, la soluzione è ottenere un Enumerator per il IList e chiama MoveNext su di esso per ogni iterazione:

    public static IEnumerable<T> Skip<T>(this IList<T> source, int count)
    {
        using (var e = source.GetEnumerator())
            while (count < source.Count && e.MoveNext())
                yield return source[count++];
    }

In questo modo ottieni un comportamento O (1) dall'indicizzazione, ma ottieni comunque tutto il comportamento di lancio delle eccezioni della chiamata MoveNext. Nota che chiamiamo solo MoveNext per l'eccezione degli effetti collaterali; Ignoriamo i valori su cui elenca.

Altri suggerimenti

Il Readonlycolection La classe potrebbe aiutarti con la tua collezione immutabile.

Il mio consiglio: personalmente non proverei a "ingannare" il compilatore a meno che tu non abbia un problema di prestazioni. Non si sa mai, la prossima versione potrebbe far funzionare il tuo codice ottimizzato due volte più lento dell'originale. Non ottimizzare preventivamente. I metodi forniti nel framework possono produrre un codice davvero ottimizzato che sarebbe difficile da reimplementare.

qui è un articolo di MSDN che fornisce informazioni su quali raccolte utilizzare per scopi diversi. Userei una raccolta appropriata per l'attività invece di provare a ottimizzare saltare e prendere.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top