web-dev-qa-db-fra.com

Compteur dans la boucle foreach en C #

Fonctionnement de foreach: Comme je le sais,

foreach est une boucle qui parcourt une collection ou un tableau un par un, à partir de l'index 0 jusqu'au dernier élément de la collection.

Donc, si j'ai n éléments dans un tableau.

foreach (var item in arr)
{

}  

puis, In, 1ère itération, item = arr [0];
puis, au 2e, item = arr [1];
.
.
.
en dernier (nième), item = arr [n-1];

Conclusion: de travailler, il semble qu'à chaque itération, il sait quelle valeur à prendre du tableau ou il connaît l'indice de l'élément à prendre du tableau.

Maintenant ma question: Comment puis-je obtenir l'index d'un élément sans utiliser une nouvelle variable?

foreach (string item in mylist)
{
   if (item == "myitem")
   {
       // get index of item
       break;
   }
}
41
Javed Akram

Cela dépend de ce que vous entendez par "ça". L'itérateur sait à quel index il est atteint, oui - dans le cas d'un List<T> Ou d'un tableau. Mais il n'y a pas d'index général dans IEnumerator<T>. Qu'il s'agisse d'itérer sur une collection indexée ou non dépend de l'implémentation. De nombreuses collections ne prennent pas en charge l'indexation directe.

(En fait, foreach n'utilise pas toujours du tout un itérateur. Si le type de compilation de la collection est un tableau, le compilateur va itérer dessus en utilisant array[0], array[1] Etc. De même, la collection peut avoir une méthode appelée GetEnumerator() qui retourne un type avec les membres appropriés, mais sans aucune implémentation de IEnumerable/IEnumerator en vue. )

Options pour maintenir un index:

  • Utilisez une boucle for
  • Utilisez une variable distincte
  • Utilisez une projection qui projette chaque élément sur une paire indice/valeur, par ex.

     foreach (var x in list.Select((value, index) => new { value, index }))
     {
         // Use x.value and x.index in here
     }
    
  • Utilisez ma classe SmartEnumerable qui est un peu comme l'option précédente

Toutes les options, sauf la première, fonctionneront, que la collection soit ou non indexée naturellement.

81
Jon Skeet

Utilisez for au lieu de foreach. foreach n'expose pas son fonctionnement interne, il énumère tout ce qui est IEnumerable (qui n'a pas du tout besoin d'avoir un index).

for (int i=0; i<arr.Length; i++)
{
    ...
}

De plus, si ce que vous essayez de faire est de trouver l'index d'un élément particulier dans la liste, vous n'avez pas du tout à l'itérer par vous-même. Utilisez à la place Array.IndexOf(item).

18
Yodan Tauber

Votre compréhension de foreach est incomplète.

Il fonctionne avec tout type qui expose IEnumerable (ou implémente une méthode GetEnumerable) et utilise le IEnumerator renvoyé pour parcourir les éléments de la collection.

La façon dont Enumerator fait cela (en utilisant un index, une instruction yield ou magique) est un détail d'implémentation.

Pour obtenir ce que vous voulez, vous devez utiliser une boucle for:

for (int i = 0; i < mylist.Count; i++)
{
}

Remarque:

Obtenir le nombre d'éléments dans une liste est légèrement différent selon le type de liste

For Collections: Use Count   [property]
For Arrays:      Use Length  [property]
For IEnumerable: Use Count() [Linq method]
11
Oded

Ou encore plus simple si vous ne voulez pas utiliser beaucoup de linq et pour une raison quelconque, vous ne voulez pas utiliser de boucle for.

int i = 0;
foreach(var x in arr)
{
   //Do some stuff
   i++;
}
7
BlueVoodoo

Probablement inutile, mais ...

foreach (var item in yourList.Select((Value, Index) => new { Value, Index }))
{
    Console.WriteLine("Value=" + item.Value + ", Index=" + item.Index);
}
2
LukeH

Cela n'est vrai que si vous parcourez un tableau; Et si vous parcouriez un autre type de collection qui n'a aucune notion d'accès par index? Dans le cas d'un tableau, la façon la plus simple de conserver l'index est d'utiliser simplement une boucle Vanilla for.

1
Dan Bryant

Sans version Foreach personnalisée:

datas.Where((data, index) =>
{
    //Your Logic
    return false;
}).Any();

Dans certains cas simples, ma manière utilise where + false + any.
Il est un peu plus gros que foreach + select((data,index)=>new{data,index}), et sans méthode Foreach personnalisée.

MyLogic:

  • utilisez corps de l'instruction exécutez votre logique.
  • car return false, le nouveau nombre de données énumérables est zéro.
  • utilisez Any () laissez yeild courir.

Code de test de référence

[RPlotExporter, RankColumn]
public class BenchmarkTest
{
    public static IEnumerable<dynamic> TestDatas = Enumerable.Range(1, 10).Select((data, index) => $"item_no_{index}");

    [Benchmark]
    public static void ToArrayAndFor()
    {
        var datats = TestDatas.ToArray();
        for (int index = 0; index < datats.Length; index++)
        {
            var result = $"{datats[index]}{index}";
        }
    }

    [Benchmark]
    public static void IEnumrableAndForach()
    {
        var index = 0;
        foreach (var item in TestDatas)
        {
            index++;
            var result = $"{item}{index}";
        }
    }

    [Benchmark]
    public static void LinqSelectForach()
    {
        foreach (var item in TestDatas.Select((data, index) => new { index, data }))
        {
            var result = $"{item.data}{item.index}";
        }
    }

    [Benchmark]
    public static void LinqSelectStatementBodyToList()
    {
        TestDatas.Select((data, index) =>
        {
            var result = $"{data}{index}";
            return true;
        }).ToList();
    }

    [Benchmark]
    public static void LinqSelectStatementBodyToArray()
    {
        TestDatas.Select((data, index) =>
        {
            var result = $"{data}{index}";
            return true;
        }).ToArray();
    }

    [Benchmark]
    public static void LinqWhereStatementBodyAny()
    {
        TestDatas.Where((data, index) =>
        {
            var result = $"{data}{index}";
            return false;
        }).Any();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<BenchmarkTest>();

        System.Console.Read();
    }
}

Résultat de référence:

                         Method |     Mean |     Error |    StdDev | Rank |
------------------------------- |---------:|----------:|----------:|-----:|
                  ToArrayAndFor | 4.027 us | 0.0797 us | 0.1241 us |    4 |
            IEnumrableAndForach | 3.494 us | 0.0321 us | 0.0285 us |    1 |
               LinqSelectForach | 3.842 us | 0.0503 us | 0.0471 us |    3 |
  LinqSelectStatementBodyToList | 3.822 us | 0.0416 us | 0.0389 us |    3 |
 LinqSelectStatementBodyToArray | 3.857 us | 0.0764 us | 0.0785 us |    3 |
      LinqWhereStatementBodyAny | 3.643 us | 0.0693 us | 0.0712 us |    2 |
1
IT WeiHan

Toutes les collections n'ont pas d'index. Par exemple, je peux utiliser un Dictionary avec foreach (et parcourir toutes les clés et valeurs), mais je ne peux pas écrire get sur des éléments individuels en utilisant dictionary[0], dictionary[1] etc.

Si je voulais parcourir un dictionnaire et garder la trace d'un index, je devrais utiliser une variable distincte que j'ai moi-même incrémentée.

0
Tim Robinson

La séquence en cours d'itération dans une boucle foreach peut ne pas prendre en charge l'indexation ou connaître un tel concept dont elle a juste besoin pour implémenter une méthode appelée GetEnumerator qui renvoie un objet qui a au minimum l'interface d'IEnumerator, bien qu'il ne soit pas nécessaire de l'implémenter. Si vous savez que ce que vous itérez prend en charge l'indexation et que vous avez besoin de l'index, je suggère d'utiliser une boucle for à la place.

Un exemple de classe qui peut être utilisé dans foreach:

    class Foo {

        public iterator GetEnumerator() {
            return new iterator();
        }

        public class iterator {
            public Bar Current {
                get{magic}
            }

            public bool MoveNext() {
                incantation
            }
        }
    }
0
Rune FS

Depuis MSDN:

L'instruction foreach répète un groupe d'instructions incorporées pour chaque élément d'un tableau ou d'une collection d'objets qui implémente l'interface System.Collections.IEnumerable ou System.Collections.Generic.IEnumerable (Of T).

Donc, ce n'est pas nécessairement Array. Il pourrait même s'agir d'une collection paresseuse sans aucune idée du nombre d'éléments de la collection.

0
Manish Basantani