2 votes

Utiliser .First() et .Count() pour briser un IEnumerable ?

Voici mon code :

protected IEnumerable<Hotel> Hotels;
protected Hotel Hotel;

Hotels = from Hotel hotel in new Hotels()
         select hotel;

Response.Write("RES " + Hotels.Count() + "<br />");
if (Hotels.Count() > 0)
{
    Hotel = Hotels.First();
}
Response.Write("RES " + Hotels.Count() + "<br />");
Response.Write("RES " + Hotels.Count() + "<br />");

Eh bien, les hôtels ont un article dessus. Mais le résultat est :

RES 1
RES 0
RES 1

Pourquoi ? Il semble que .First() faire une confusion avec l'itérateur ? Comment puis-je y remédier en utilisant IEnumberable ? (sans utiliser un autre type de liste, j'ai besoin de IEnumerable).

3voto

Luke McGregor Points 11824
static void Main(string[] args)
{
    IEnumerable<String> Hotels = new List<String>{"sdsfsdf"};
    String Hotel;

    Console.WriteLine("RES " + Hotels.Count());
    if (Hotels.Count() > 0)
    {
        Hotel = Hotels.First();
    }
    Console.WriteLine("RES " + Hotels.Count());
    Console.WriteLine("RES " + Hotels.Count());
}

imprime

RES 1
RES 1
RES 1

Comme prévu, comment remplissez-vous l'énumérable et sous quel type le créez-vous ?

Si vous remplissez ce formulaire via un quérable non exécuté, vous risquez d'obtenir un comportement bizarre comme celui-ci, car il choisira la première utilisation (c'est à dire First() dans ce cas).

3voto

sll Points 30638

Pas de magie ici. On dirait que la quantité d'articles dans le Hotels collection changeant dans le temps, peut-être une requête LINQ avec exécution différée, peut-être même LINQ-to-SQL.

Veuillez montrer un code complet qui remplit les hôtels.

1voto

faester Points 6055

La méthode "First" fait probablement avancer l'itérateur pour qu'il pointe sur le deuxième élément. Votre premier appel à "Count" termine alors l'itération sur votre séquence d'un élément, et l'appel suivant à Count recommence l'itération. Count itérera toujours la séquence entière, il est donc idempotent. Vous devez trouver un moyen de réinitialiser l'énumérateur. Hotels.Reset() après First() de faire Count() se comportent correctement.

Le plus simple serait sans doute de faire un Reset vous-même, mais je ne peux pas reproduire votre problème. Si vous avez un itérateur de base de données généré par le système, il est possible qu'il ne soit pas réinitialisé automatiquement et vous pourriez utiliser le code ci-dessous. Il s'agit simplement d'utiliser votre propre constatation qu'un simple Count() réinitialise l'itérateur. Et ce serait O(n) à compléter.

static class Extensions
{
    public static void Reset<T>(this IEnumerable<T> toReset )
    {
        if (toReset != null)
        {
            int i = toReset.Count();
        }
    }
}

Mais je ne peux pas reproduire votre problème :

Le code ci-dessous donne le résultat correct partout. Avez-vous écrit votre propre énumérateur ou collection ?

static void Main(string[] args)
{
    var Response = System.Console.Out;

    var Hotels = new[]{1, 2, 3, 4};
    var Hotel = 0;

    Response.Write("RES " + Hotels.Count() + "<br />");
    if (Hotels.Count() > 0)
    {
        Hotel = Hotels.First();
    }
    Response.Write("RES " + Hotels.Count() + "<br />");
    Response.Write("RES " + Hotels.Count() + "<br />");

    Console.WriteLine( "Hotel: " + Hotel);
}

1voto

Joachim Isaksson Points 85969

Selon l'implémentation sous-jacente, la ré-énumération d'un IEnumerable n'est en aucun cas garantie de retourner la même valeur. Pour comprendre pourquoi, prenez cet exemple ;

static void Main(string[] args)
{
    IEnumerable<int> bop = RandomSequence();
    Console.WriteLine(bop.Count());
    Console.WriteLine(bop.Count());
}

private static int _seed = 0;
static IEnumerable<int> RandomSequence()
{
    var random = new Random(_seed++);
    int randomNumber;
    while ((randomNumber = random.Next(100)) != 0)
        yield return randomNumber;
} 

C'est un IEnumerable parfaitement valide, et les deux appels à Count() donneront lieu à deux valeurs pseudo-aléatoires différentes. La raison en est que Count() ré-énumère le même IEnumerable et génère une séquence aléatoire complètement différente.

Si vous voulez des énumérations répétables, vous devrez appeler ToList() ou ToArray sur l'énumérable pour stocker les résultats, et faire toutes les énumérations à partir de la liste/du tableau qui énumère de la même façon à chaque fois.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X