2

Is there a way to know the current run of a foreach without have to:

Int32 i;
foreach
   i++;

or is that the best option I got? Also, how can I know the maximum number of items on that loop? What I am trying to do is update a progressbar during a foreach loop on my form.

This is what I have so far:

    FileInfo[] directoryFiles = (new DirectoryInfo(folderBrowserDialog.SelectedPath)).GetFiles("*.*");
    foreach (FileInfo file in directoryFiles)
    {
        if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden || (file.Attributes & FileAttributes.System) == FileAttributes.System)
            continue;

        backgroundWorkerLoadDir.ReportProgress(i, file.Name);
        System.Threading.Thread.Sleep(10);
    }

So it should be like the following, right?

   for (Int32 i = 0; i < DirectoryFiles.Length; i++)
   {
       if ((DirectoryFiles[i].Attributes & FileAttributes.Hidden) == FileAttributes.Hidden || (DirectoryFiles[i].Attributes & FileAttributes.System) == FileAttributes.System)
           continue;

       backgroundWorkerLoadDir.ReportProgress((i / DirectoryFiles.Length) * 100, DirectoryFiles[i].Name);
       System.Threading.Thread.Sleep(10);
   }
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Eyla
  • 21
  • 1
  • 1
  • 3
  • 2
    Could you paste your current code, including the declaration for the collection that you're iterating? – Reinderien Jan 01 '11 at 22:43
  • 2
    Please post some real code, not pseudo-code. – Martin v. Löwis Jan 01 '11 at 22:43
  • what is the type of array /collection u are looping through? – Tasawer Khan Jan 01 '11 at 22:48
  • If i use the For loop, how can i make it doesn't count on the hidden files and system ones? would i need to make another loop to count the number of files 1st? – Eyla Jan 01 '11 at 22:55
  • what is type of DirectoryFiles? – Tasawer Khan Jan 01 '11 at 22:58
  • Sorry, forgot that, just added it. – Eyla Jan 01 '11 at 23:00
  • ok so it is an array use for loop as suggested in my answer. DirectoryFiles.Length will count all in array. So u know in advance how many elements are there. – Tasawer Khan Jan 01 '11 at 23:04
  • possible duplicate of [(C#) Get index of current foreach iteration](http://stackoverflow.com/questions/43021/c-get-index-of-current-foreach-iteration) – sloth Jun 12 '12 at 14:07
  • Looks like what you are after is something like [enumerate](http://docs.python.org/library/functions.html#enumerate "enumerate") in Python, however there is not a C# equivalent. The other answers given outline your options in C#. **Update** Actually, this is discussed in another post - http://stackoverflow.com/questions/521687/c-foreach-with-index – Jared Knipp Jan 01 '11 at 22:49
  • The canonical question is *[How do you get the index of the current iteration of a foreach loop?](http://stackoverflow.com/questions/43021)*. – Peter Mortensen Jul 28 '15 at 12:15

6 Answers6

7

Foreach is a fairly big deal, its underlying plumbing is a very simple forward-only iterator that doesn't require having to know up front how many items are in the collection. IEnumerator<>.

Which is in general is a rather nasty mismatch with a ProgressBar. You can only meaningfully update the standard one when you know up front how many items you're iterating. So you can set its Maximum property. If that's possible then you no doubt have access to a Count property and have an indexer as well. Which then lets you use a for() statement, problem solved.

But it is pretty common that you have no idea up front how many items there are to iterate. Typical with dbase queries for example. Since you can't find out how long it might take to iterate, you've only got one option on the progress bar: Style = Marquee. The "I'm not dead, I'm working" way of reporting progress. Problem solved.

Hans Passant
  • 873,011
  • 131
  • 1,552
  • 2,371
6

Use for loop instead of foreach. That is better in this situation.

Use it like this:

count = DirectoryFiles.Length;
for (int k = 0; k < count; k++)
{
    FileInfo file = DirectoryFiles[k];
    if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden || 
        (file.Attributes & FileAttributes.System) == FileAttributes.System)
    {
        continue;
    }

    backgroundWorkerLoadDir.ReportProgress(i, file.Name);
    System.Threading.Thread.Sleep(10);
}
gunr2171
  • 10,315
  • 25
  • 52
  • 75
Tasawer Khan
  • 5,305
  • 6
  • 42
  • 66
2

No, it is impossible for IEnumerable, because it has not Count/Length property. Moreover, you can easily imagine infinite IEnumerable sequence, like:

private readonly Random rand = new Random();
private IEnumerable<int> GetInfiniteRandomSequence() 
{
    while(true)
    {
        yield return rand.Next();
    }
}
Victor Haydin
  • 3,398
  • 1
  • 23
  • 38
1

The basic principle for this problem is the following:

List<string> numbers = new List<string>() { "one", "two", "three" };

foreach (var num in numbers.Select((value, index) => new { value, index }))
{
    Console.WriteLine("Value: {0}, Index: {1}", num.value, num.index);
}

This will create a new anonymous type within the foreach loop that contains the value and index within the num variable.

So to solve the problem provided in the OP:

FileInfo[] directoryFiles = (new DirectoryInfo(folderBrowserDialog.SelectedPath)).GetFiles("*.*");
foreach (FileInfo file in directoryFiles.Select((value, index) => new {value, index}))
{
    if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden
          ||
        (file.Attributes & FileAttributes.System) == FileAttributes.System)
        continue;

    int progress = (file.index / DirectoryFiles.Length) * 100;

    backgroundWorkerLoadDir.ReportProgress(progress, file.value.Name);
    System.Threading.Thread.Sleep(10);
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
naed21
  • 149
  • 4
  • 9
1

That's your best option, because standard for loops don't work on IEnumerables that are not indexable. If you need to know the number of items, use the Count property; if none is available, then obviously by definition you can't know ahead of time.

user541686
  • 189,354
  • 112
  • 476
  • 821
1
using System.Linq;

for (var i = 0; i < items.Count(); i++)
{
    // Update progress bar here.
}
Michael S. Scherotter
  • 10,500
  • 3
  • 31
  • 54
  • I added the using directive and changed the Count to a method. That will work for ICollection containers. – Michael S. Scherotter Jan 01 '11 at 22:49
  • Now it's only for .NET 3.5+... :) and by the way, that is *very* slow because I don't think the JIT compiler can see far enough to only evaluate items.Count() once, so it'll evaluate it multiple times, resulting in a dramatic speed decrease for some collections. You'd need to cache the variable in the beginning. :) – user541686 Jan 01 '11 at 22:53
  • Someone once tell me than for loop is always faster than foreach loop? But https://stackoverflow.com/questions/365615/in-net-which-loop-runs-faster-for-or-foreach – Bastien Vandamme Aug 11 '17 at 00:49