Elenco C # < T > vs IEnumerable < T > domanda di prestazione
-
06-07-2019 - |
Domanda
Ciao supponiamo che questi 2 metodi:
private List<IObjectProvider> GetProviderForType(Type type)
{
List<IObjectProvider> returnValue = new List<IObjectProvider>();
foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders)
{
if ((provider.Key.IsAssignableFrom(type) ||
type.IsAssignableFrom(provider.Key)) &&
provider.Value.SupportsType(type))
{
returnValue.Add(provider.Value);
}
}
return returnValue;
}
private IEnumerable<IObjectProvider> GetProviderForType1(Type type)
{
foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders)
if ((provider.Key.IsAssignableFrom(type) ||
type.IsAssignableFrom(provider.Key)) &&
provider.Value.SupportsType(type))
yield return provider.Value;
}
Quale è più veloce? Quando guardo il primo metodo, vedo che la memoria è allocata per List, ciò che secondo me non è necessario. Il metodo IEnumerable sembra essere più veloce per me.
Ad esempio, supponi di chiamare
int a = GetProviderForType(myType).Count;
int b = GetProviderForType1(myType).Count();
Ora, un altro problema è, c'è una differenza di prestazioni tra questi 2 sopra?
Cosa ne pensi?
Soluzione
In questo caso particolare, l'utilizzo del modulo IEnumerable < T >
sarà più efficiente, poiché solo è necessario conoscere il conteggio. Non ha senso archiviare i dati, ridimensionare i buffer, ecc., Se non è necessario.
Se fosse necessario utilizzare nuovamente i risultati per qualsiasi motivo, il modulo Elenco < T >
sarebbe più efficiente.
Nota che sia il metodo di estensione Count ()
che la proprietà Count
saranno efficienti per List < T >
come implementazione di < code> Count () verifica se la sequenza target implementa ICollection < T >
e, in tal caso, utilizza la proprietà Count
.
Un'altra opzione che dovrebbe essere più efficiente (anche se solo giusta) sarebbe quella di chiamare il sovraccarico di Count
che accetta un delegato:
private int GetProviderCount(Type type)
{
return _objectProviders.Count(provider =>
(provider.Key.IsAssignableFrom(type)
|| type.IsAssignableFrom(provider.Key))
&& provider.Value.SupportsType(type));
}
Ciò eviterà il livello extra di indicazioni indirette sostenute dalle clausole Where
e Select
.
(Come dice Marc, per piccole quantità di dati le differenze di prestazioni saranno probabilmente comunque trascurabili.)
Altri suggerimenti
La risposta precisa a domande come questa può variare a seconda di molti fattori e può cambiare ulteriormente con l'evoluzione del CLR. L'unico modo per essere sicuri è misurarlo - e tieni presente che se la differenza è piccola rispetto all'operazione in cui apparirà, dovresti scegliere il modo più leggibile e sostenibile di scriverlo.
E su quella nota, potresti anche provare:
private IEnumerable<IObjectProvider> GetProviderForType1(Type type)
{
return _objectProviders.Where(provider =>
provider.Key.IsAssignableFrom(type) ||
type.IsAssignableFrom(provider.Key)) &&
provider.Value.SupportsType(type))
.Select(p => p.Value);
}
Puoi anche darti molta flessibilità restituendo IEnumerable < T >
e quindi usando il metodo di estensione ToList
se vuoi " snapshot " i risultati in un elenco. Ciò eviterà una valutazione ripetuta del codice per generare l'elenco, se è necessario esaminarlo più volte.
Una parte importante di questa domanda è "quanto sono grandi i dati"? Quante righe ...
Per piccole quantità di dati, l'elenco va bene - ci vorrà un tempo trascurabile per allocare un elenco abbastanza grande, e non si ridimensionerà molte volte (nessuno, se si può dire quanto sia grande in anticipo).
Tuttavia, questo non si adatta a enormi volumi di dati; sembra improbabile che il tuo provider supporti migliaia di interfacce, quindi non direi che necessario andare a questo modello - ma non farà molto male.
Ovviamente puoi usare anche LINQ:
return from provider in _objectProviders
where provider.Key.IsAssignableFrom(type) ...
select provider.Value;
Questo è anche l'approccio differito rendimento
sotto le copertine ...
La principale differenza tra IEnumerable e IList:
IEnumerable: Implementa MoveNext, Reimposta, Ottieni metodi correnti e restituisce un tipo di IEnumerator da Iterate Attraverso la documentazione.
IList: espone l'interfaccia IEnumerable e anche una raccolta di oggetti non generici a cui è possibile accedere tramite l'indice, quindi IEnumerable + ICollection (Manipolazione dei dati) e aggiungere, rimuovere, inserire (in un indice specifico) sono utili metodi implementati da IList.
Dopo aver esaminato il tuo codice a mio parere, IEnumerable è più efficiente ma l'elenco di ritorno è utile anche se vuoi manipolare i dati e se vuoi semplicemente scorrere i dati, è preferibile IEnumerable.