C # foreach miglioramenti?

9

Mi capita spesso durante la programmazione in cui voglio avere un indice di conteggio dei cicli all'interno di un foreach e devo creare un numero intero, usarlo, incrementarlo, ecc. Non sarebbe una buona idea se ci fosse una parola chiave? ha introdotto che era il conteggio dei cicli all'interno di un foreach? Potrebbe anche essere usato anche in altri loop.

Questo va contro l'uso di parole chiave visto come il compilatore non permetterebbe questa parola chiave usata ovunque ma in un costrutto di loop?

    
posta Justin 14.06.2012 - 14:51
fonte

8 risposte

54

Se hai bisogno di un conteggio dei cicli all'interno di un ciclo foreach , perché non usi semplicemente un normale ciclo for . Il ciclo foreach è stato concepito per rendere più semplici gli usi specifici di for loop. Sembra che tu abbia una situazione in cui la semplicità di foreach non è più vantaggiosa.

    
risposta data 14.06.2012 - 15:01
fonte
37

Puoi usare tipi anonimi come questo

foreach (var item in items.Select((v, i) => new { Value = v, Index = i }))
{
    Console.WriteLine("Item value is {0} and index is {1}.", item.Value, item.Index);
}

Questo è simile alla funzione enumerate di Python

for i, v in enumerate(items):
    print v, i
    
risposta data 14.06.2012 - 15:04
fonte
13

Sto solo aggiungendo la risposta di Dan Finch qui, come richiesto.

Non darmi punti, dai punti a Dan Finch: -)

La soluzione è scrivere il seguente metodo di estensione generico:

public static void Each<T>( this IEnumerable<T> ie, Action<T, int> action )
{
    var i = 0;
    foreach ( var e in ie ) action( e, i++ );
}

Che viene utilizzato in questo modo:

var strings = new List<string>();
strings.Each( ( str, n ) =>
{
    // hooray
} );
    
risposta data 14.06.2012 - 16:31
fonte
5

Potrei sbagliarmi, ma ho sempre pensato che il punto principale del ciclo foreach fosse semplificare l'iterazione dai giorni STL di C ++ i.e.

for(std::stltype<typename>::iterator iter = stl_type_instance.begin(); iter != stl_type_instance.end(); iter++)
{
   //dereference iter to use each object.
}

confronta questo con l'uso di .NET di IEnumerable in foreach

foreach(TypeName instance in DescendentOfIEnumerable)
{
   //use instance to use the object.
}

quest'ultimo è molto più semplice ed evita molte delle trappole precedenti. Inoltre, sembrano seguire regole quasi identiche. Ad esempio, non è possibile modificare i contenuti IEnumerable mentre si è all'interno del ciclo (che ha molto più senso se si pensa al modo STL). Tuttavia, il ciclo for ha spesso uno scopo logico diverso dall'iterazione.

    
risposta data 14.06.2012 - 15:30
fonte
2

Se la raccolta in questione è un IList<T> , puoi semplicemente utilizzare for con l'indicizzazione:

for (int i = 0; i < collection.Count; i++)
{
    var item = collection[i];
    // …
}

In caso contrario, potresti usare Select() , ha un sovraccarico che ti dà l'indice.

Se anche questo non è adatto, penso che mantenere quell'indice manualmente sia abbastanza semplice, sono solo due righe di codice molto brevi. Creare una parola chiave appositamente per questo sarebbe un eccesso.

int i = 0;
foreach (var item in collection)
{
    // …
    i++;
}
    
risposta data 14.06.2012 - 17:05
fonte
2

Se tutto quello che sai è che la raccolta è un oggetto IEnumerable, ma devi tenere traccia di quanti elementi hai elaborato finora (e quindi il totale quando hai finito), puoi aggiungere un paio di linee a un basic per loop:

var coll = GetMyCollectionAsAnIEnumerable();
var idx = 0;
for(var e = coll.GetEnumerator(); e.MoveNext(); idx++)
{
   var elem = e.Current;

   //use elem and idx as you please
}

Puoi anche aggiungere una variabile di indice incrementata a un foreach:

var i=0;
foreach(var elem in coll)
{
   //do your thing, then...
   i++;
}

Se vuoi rendere questo aspetto più elegante, puoi definire un metodo di estensione o due per "nascondere" questi dettagli:

public static void ForEach<T>(this IEnumerable<T> input, Action<T> action)
{
    foreach(T elem in input)
        action(elem);
}

public static void ForEach<T>(this IEnumerable<T> input, Action<T, int> action)
{
    var idx = 0;
    foreach(T elem in input)
        action(elem, idx++); //post-increment happens after parameter-passing
}

//usage of the index-supporting method
coll.ForEach((e, i)=>Console.WriteLine("Element " + (i+1) + ": " + e.ToString()));
    
risposta data 14.06.2012 - 19:26
fonte
1

Anche lo scope funziona se vuoi mantenere il blocco pulito delle collisioni con altri nomi ...

{ int index = 0; foreach(var el in List) { Console.WriteLine(el + " @ " + index++); } }
    
risposta data 14.06.2012 - 23:30
fonte
-1

Per questo uso un ciclo while . Anche i loop con for funzionano e molte persone sembrano preferirli, ma mi piace solo usare for con qualcosa che avrà un numero fisso di iterazioni. IEnumerable s può essere generata in fase di runtime, quindi, a mio parere, while ha più senso. Chiamalo come una linea di codice informale, se ti va.

    
risposta data 02.03.2016 - 18:47
fonte

Leggi altre domande sui tag