简体   繁体   中英

C# foreach loop not executing

I have the following code that only executes the first foreach loop. When I go into the step by step mode in Visual Studio, the program just skips over every subsequent foreach loop. Why is this happening, and is there a workaround? Is there a way to cycle through an IEnumerable<> without using a foreach loop (like a for loop, although I haven't figured out how to do that).

using CsvHelper;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;

...

    public class State
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Pop { get; set; }
        public string Ab { get; set; }
        public int Reps { get; set; }
        public double standardQuota { get; set; }
        public int lowerQuota { get; set; }
        public int upperQuota { get; set; }
    }

...

        static void currentRule(int num)
        {
            IEnumerable<State> records = null;
            int size = 0;
            Console.WriteLine("Please enter a size for the house, greater than the number of states. Current is 435. select 1 for wyoming rule or 2 for cube root.");
            string s = Console.ReadLine();
            size = Int32.Parse(s);
            if (num == 1)
            {
                string path = "C:/.../pop50.csv";
                using (var reader = new StreamReader(path))
                using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
                {
                    records = csv.GetRecords<State>();
                    int total = 0;
                    foreach (var state in records)
                    {
                        total += state.Pop;
                    }
                    var standardDivisor = total / size;
                    foreach (var state in records)
                    {
                        state.standardQuota = state.Pop / standardDivisor;
                        state.lowerQuota = (int)Math.Floor(state.standardQuota);
                        state.upperQuota = (int)Math.Ceiling(state.standardQuota);
                        state.Reps = 1;
                        size -= 1;
                    }
                    while (size > 0)
                    {
                        double max = 0;
                        double maxIndex = 0;
                        int i = -1;
                        foreach (var state in records)
                        {
                            i++;
                            var numSeats = state.Reps;
                            var div = state.Pop / Math.Sqrt(size * (size + 1));
                            if (div > max)
                            {
                                max = div;
                                maxIndex = i;
                            }
                        }
                        foreach (var state in records)
                        {
                            if (state.Id == maxIndex)
                            {
                                state.Reps++;
                                size--;
                            }
                        }
                            
                    }
                }
            }
            print(records);
        }

GetRecords returns a stateful IEnumerable the yields a single record at the time. The operative word here is stateful - once you've consumed a record (eg, by iterating over it with a foreach loop), it's gone.

There are a few approaches you could take here:

  1. Collapse all your in to a single foreach loop instead of three different loops which perform different parts of the logic.
  2. Call GetRecords every time you need to iterate over the records. Note that this will reread the file, so you'll be performing triple the I/O.
  3. Convert the iterable to a List (eg, by using Linq's ToList() ), and then iterate over it whenever you wish. Note that this means you'll be holding the entire file's contents in memory isntead of just a small portion of it.

Is there a way to cycle through a IEnumerable<> without using a foreach loop (like a for loop, although I haven't figured out how to do that).

Try this to get the enumerator:


Option 1 with Enumerator :

using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{        
 var records = csv.GetRecords<State>();
 var enumerator = records.GetEnumerator();
 while(enumerator.MoveNext())
 {
    var currItemEn = enumerator.Current;            
 }
}

Option 2 with a List :

// or if you want a list, save this list and reuse it!
records = csv.GetRecords<State>().ToList<State>();

Ref Doc: Suggestion, I prefer to get the object list its onetime, easier and better. Based on the ref. from CsvHelper Page

Looks like you have multiple loops and resulting in reuse the Enumerable ..

Convert CSV rows into a class object that is re-used on every iteration of the enumerable. Each enumeration will hydrate the given record, but only the mapped members. If you supplied a map and didn't map one of the members, that member will ---- > not get hydrated with the current row's data. ----> Be careful. Any methods that you call on the projection that force the evaluation of the IEnumerable, such as ToList(), you will get a list where all the records are the same instance you provided that is hydrated with the last record in the CSV file.

To answer the second part of your question. You can add:

using System.Linq;

and change this code:

foreach(var state in records)
{
   total += state.Pop;
 }

To this code:

int total = records.Sum(X=> state.Pop);

Goodluck!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM