Domanda

Qual è il modo migliore per scorrere un elenco generico fortemente tipizzato in C#.NET e VB.NET?

È stato utile?

Soluzione

Per C#:

foreach(ObjectType objectItem in objectTypeList)
{
    // ...do some stuff
}

Risposta per VB.NET da Formica Viola:

For Each objectItem as ObjectType in objectTypeList
    'Do some stuff '
Next

Altri suggerimenti

Con qualsiasi implementazione generica di IEnumerable il modo migliore è:

//C#
foreach( var item in listVariable) {
    //do stuff
}

C'è però un'importante eccezione.IEnumerable comporta un sovraccarico di Current() e MoveNext() che è ciò in cui viene effettivamente compilato il ciclo foreach.

Quando hai un semplice array di strutture:

//C#
int[] valueTypeArray;
for(int i=0; i < valueTypeArray.Length; ++i) {
     int item = valueTypeArray[i];
     //do stuff
}

È più veloce.


Aggiornamento

A seguito di una discussione con @Steven Sudit (vedi commenti) penso che il mio consiglio originale potrebbe non essere aggiornato o errato, quindi ho eseguito alcuni test:

// create a list to test with
var theList = Enumerable.Range(0, 100000000).ToList();

// time foreach
var sw = Stopwatch.StartNew();
foreach (var item in theList)
{
    int inLoop = item;
}
Console.WriteLine("list  foreach: " + sw.Elapsed.ToString());

sw.Reset();
sw.Start();

// time for
int cnt = theList.Count;
for (int i = 0; i < cnt; i++)
{
    int inLoop = theList[i];
}
Console.WriteLine("list  for    : " + sw.Elapsed.ToString());

// now run the same tests, but with an array
var theArray = theList.ToArray();

sw.Reset();
sw.Start();

foreach (var item in theArray)
{
    int inLoop = item;
}
Console.WriteLine("array foreach: " + sw.Elapsed.ToString());

sw.Reset();
sw.Start();

// time for
cnt = theArray.Length;
for (int i = 0; i < cnt; i++)
{
    int inLoop = theArray[i];
}
Console.WriteLine("array for    : " + sw.Elapsed.ToString());

Console.ReadKey();

Quindi, ho eseguito questo nel rilascio con tutte le ottimizzazioni:

list  foreach: 00:00:00.5137506
list  for    : 00:00:00.2417709
array foreach: 00:00:00.1085653
array for    : 00:00:00.0954890

E poi esegui il debug senza ottimizzazioni:

list  foreach: 00:00:01.1289015
list  for    : 00:00:00.9945345
array foreach: 00:00:00.6405422
array for    : 00:00:00.4913245

Quindi sembra abbastanza coerente, for è più veloce di foreach e gli array sono più veloci degli elenchi generici.

Tuttavia, questo avviene su 100.000.000 di iterazioni e la differenza è di circa 0,4 di secondo tra il metodo più veloce e quello più lento.A meno che tu non stia eseguendo enormi loop critici per le prestazioni, non vale la pena preoccuparsene.

Per VB.NET:

For Each tmpObject as ObjectType in ObjectTypeList
    'Do some stuff '
Next

C#

myList<string>().ForEach(
    delegate(string name)
    {
        Console.WriteLine(name);
    });

I delegati anonimi non sono attualmente implementati in VB.Net, ma sia C# che VB.Net dovrebbero essere in grado di eseguire lambda:

C#

myList<string>().ForEach(name => Console.WriteLine(name));

VB.Net

myList(Of String)().ForEach(Function(name) Console.WriteLine(name))

Come ha sottolineato Grauenwolf, il VB sopra non verrà compilato poiché lambda non restituisce un valore.Un normale ciclo ForEach come altri hanno suggerito è probabilmente il più semplice per ora, ma come al solito ci vuole un blocco di codice per fare ciò che C# può fare in una riga.


Ecco un esempio banale del perché questo potrebbe essere utile:questo ti dà la possibilità di passare la logica del ciclo da un altro ambito rispetto a quello in cui esiste IEnumerable, quindi non devi nemmeno esporlo se non lo desideri.

Supponiamo che tu abbia un elenco di percorsi URL relativi che desideri rendere assoluti:

public IEnumerable<String> Paths(Func<String> formatter) {
    List<String> paths = new List<String>()
    {
        "/about", "/contact", "/services"
    };

    return paths.ForEach(formatter);
}

Quindi potresti chiamare la funzione in questo modo:

var hostname = "myhost.com";
var formatter = f => String.Format("http://{0}{1}", hostname, f);
IEnumerable<String> absolutePaths = Paths(formatter);

Dandoti "http://myhost.com/about", "http://myhost.com/contact" eccetera.Ovviamente ci sono modi migliori per ottenere questo risultato in questo esempio specifico, sto solo cercando di dimostrare il principio di base.

Senza conoscere l'implementazione interna di un elenco, penso che generalmente il modo migliore per scorrere su di esso sarebbe un ciclo foreach.Poiché foreach utilizza un IEnumerator per scorrere l'elenco, spetta all'elenco stesso determinare come spostarsi da un oggetto all'altro.

Se l'implementazione interna fosse, ad esempio, una lista concatenata, allora un semplice ciclo for sarebbe un po' più lento di un foreach.

Ha senso?

Dipende dalla tua applicazione:

  • ciclo for, se l'efficienza è una priorità
  • ciclo foreach o metodo ForEach, a seconda di quale comunica il tuo intento in modo più chiaro

Potrei perdermi qualcosa, ma scorrere un elenco generico dovrebbe essere abbastanza semplice se usi i miei esempi di seguito.La classe List<> implementa le interfacce IList e IEnumerable in modo che tu possa facilmente scorrerle praticamente nel modo desiderato.

Il modo più efficiente sarebbe utilizzare un ciclo for:

for(int i = 0; i < genericList.Count; ++i) 
{
     // Loop body
}

Puoi anche scegliere di utilizzare un ciclo foreach:

foreach(<insertTypeHere> o in genericList)
{
    // Loop body
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top