Current index of foreach loop
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 & FileAttribut开发者_StackOverflowes.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);
}
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.
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);
}
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();
}
}
That's your best option, because standard for
loops don't work on IEnumerable
s 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.
using System.Linq;
for (var i = 0; i < items.Count(); i++)
{
// Update progress bar here.
}
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);
}
精彩评论