c# boucle - foreach avec liste générique, détection de la première itération lors de l'utilisation du type de valeur




while fonction (8)

Lorsque je passe une liste générique, je veux souvent faire quelque chose de différent pour le premier élément de la liste:

List<object> objs = new List<object>
{
    new Object(),
    new Object(),
    new Object(),
    new Object()
};

foreach (object o in objs)
{
    if (o == objs.First())
    {
        System.Diagnostics.Debug.WriteLine("First object - do something special");
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("object Do something else");
    }
}

Cela va produire:

    First object - do something special
    object Do something else
    object Do something else
    object Do something else

Tout cela est bien et dandy.

Cependant, si ma liste générique est de type valeur, cette approche échouera.

List<int> ints = new List<int> { 0, 0, 0, 0 };
foreach (int i in ints)
{
    if (i == ints.First())
    {
        System.Diagnostics.Debug.WriteLine("First int - do something special");
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("int Do something else");
    }
}

Cela va produire:

    First int - do something special
    First int - do something special
    First int - do something special
    First int - do something special

Maintenant je sais que je pourrais recoder ceci pour ajouter une variable boolean ou traditionnelle for boucle, mais je me demande s'il y a moyen de savoir si une boucle foreach est sur la première itération de sa boucle.


Answers

Le problème que vous avez est parce que l'égalité pour les types de valeur est basée sur la valeur réelle, pas la référence elle-même. Plus précisément, dans votre exemple, votre liste contient tous des zéros, donc il est demandé si currentItem == firstItem, et comme ils sont tous deux à zéro, c'est toujours vrai. Pour ce genre de chose, j'ai toujours utilisé un drapeau booléen.


foreach(int i in ints.Take(1))
{
 //do first thing
}

foreach(int i in ints.Skip(1))
{
  //do other things
}

Au lieu de if (i == ints.First()) , cela fonctionne-t-il si vous utilisez if (i.Equals(ints.First()) ?


Si vous ne voulez vraiment pas utiliser un booléen, vous pouvez faire quelque chose comme:

List<int> ints = new List<int> { 0, 0, 0, 0 };

System.Diagnostics.Debug.WriteLine("First int - do something special" + ints.FirstOrDefault().ToString());
foreach (int i in ints.Skip(1))
{
    {
        System.Diagnostics.Debug.WriteLine("int Do something else");
    }
}

Mais cela semble un peu ... wierd :)


Il y a quelque temps j'ai écrit SmartEnumerable (qui fait partie de MiscUtil) qui vous permet de savoir si l'élément en cours est le premier ou le dernier, ainsi que son index. Cela peut vous aider ... cela fait partie de MiscUtil, qui est open source - vous pouvez prendre seulement SmartEnumerable sous la même licence, bien sûr.

Exemple de code (c'n'p de la page Web):

using System;
using System.Collections.Generic;

using MiscUtil.Collections;

class Example
{
    static void Main(string[] args)
    {
        List<string> list = new List<string>();
        list.Add("a");
        list.Add("b");
        list.Add("c");
        list.Add("d");
        list.Add("e");

        foreach (SmartEnumerable<string>.Entry entry in
                 new SmartEnumerable<string>(list))
        {
            Console.WriteLine ("{0,-7} {1} ({2}) {3}",
                               entry.IsLast  ? "Last ->" : "",
                               entry.Value,
                               entry.Index,
                               entry.IsFirst ? "<- First" : "");
        }
    }
}

EDIT: Notez que bien qu'il fonctionne avec des types de référence avec des références distinctes, il échouera toujours si vous lui donnez une liste où la première référence surgit ailleurs dans la liste.


Puisque vous utilisez déjà LINQ, vous pouvez le faire:

var list = new List<Int32>();

// do something with list.First();

foreach (var item in list.Skip(1))
{
    // do other stuff
}

Eh bien, vous pourriez le coder en utilisant l'itération explicite:

using(var iter = ints.GetEnumerator()) {
  if(iter.MoveNext()) {
     // do "first" with iter.Current

     while(iter.MoveNext()) {
       // do something with the rest of the data with iter.Current
     }
  }
}

L'option bool flag (avec foreach ) est probablement plus facile si ... c'est ce que je fais (presque) toujours!

Une autre option serait LINQ:

if(ints.Any()) {
  var first = ints.First();
  // do something with first
}

foreach(var item in ints.Skip(1)) {
  // do something with the rest of them
}

L'inconvénient de ce qui précède est qu'il essaie de regarder la liste 3 fois ... puisque nous savons que c'est une liste, c'est bien - mais si tout ce que nous avions était un IEnumerable<T> , il ne serait que sensé d'itérer une fois (puisque la source pourrait ne pas être re-lisible).


Considérez ceci: je l'utilise pour exporter 20 listes tapées de la même manière:

private void Generate<T>()
{
    T item = (T)Activator.CreateInstance(typeof(T));

    ((T)item as DemomigrItemList).Initialize();

    Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
    if (type == null) return;
    if (type != typeof(account)) //account is listitem in List<account>
    {
        ((T)item as DemomigrItemList).CreateCSV(type);
    }
}




c# .net