views:

3011

answers:

7

What is the best way to iterate through a strongly-typed generic List in C#.NET and VB.NET?

+18  A: 

For C#:

foreach(ObjectType objectItem in objectTypeList)
{
    // ...do some stuff
}

Answer for VB.NET from Purple Ant:

For Each objectItem as ObjectType in objectTypeList
    'Do some stuff '
Next
GoodEnough
Since it's strongly typed, you could also use: `foreach(var item in itemlist)`.
Steven Sudit
+1  A: 

I may be missing something, but iterating through a generic list should be fairly simple if you use my examples below. The List<> class implements the IList and IEnumerable interfaces so that you can easily iterate through them basically any way you want.

The most efficient way would be to use a for loop:

for(int i = 0; i < genericList.Count; ++i) 
{
     // Loop body
}

You may also choose to use a foreach loop:

foreach(<insertTypeHere> o in genericList)
{
    // Loop body
}
Dan Herbert
No need to `<insertTypeHere>`. The compiler with do it for you with `var`.
Steven Sudit
+4  A: 
Purple Ant
+2  A: 

Without knowing the internal implementation of a list, I think generally the best way to iterate over it would be a foreach loop. Because foreach uses an IEnumerator to walk over the list, it's up to the list itself to determine how to move from object to object.

If the internal implementation was, say, a linked list, then a simple for loop would be quite a bit slower than a foreach.

Does that make sense?

Matt Hamilton
Yes, because a linked list would need to search linearly for each new item, whereas an iterator over it would just take one pass.
Steven Sudit
+6  A: 

With any generic implementation of IEnumerable the best way is:

//C#
foreach( var item in listVariable) {
    //do stuff
}

There is an important exception however. IEnumerable involves an overhead of Current() and MoveNext() that is what the foreach loop is actually compiled into.

When you have a simple array of structs:

//C#
int[] valueTypeArray;
for(int i=0; i < valueTypeArray.Length; ++i) {
     int item = valueTypeArray[i];
     //do stuff
}

Is quicker.


Update

Following a discussion with @Steven Sudit (see comments) I think my original advice may be out of date or mistaken, so I ran some tests:

// create a list to test with
var theList = Enumerable.Range(0, 100000000).ToList();

// time foreach
var sw = Stopwatch.StartNew();
foreach (var item in theList)
{
    int inLoop = item;
}
Console.WriteLine("list  foreach: " + sw.Elapsed.ToString());

sw.Reset();
sw.Start();

// time for
int cnt = theList.Count;
for (int i = 0; i < cnt; i++)
{
    int inLoop = theList[i];
}
Console.WriteLine("list  for    : " + sw.Elapsed.ToString());

// now run the same tests, but with an array
var theArray = theList.ToArray();

sw.Reset();
sw.Start();

foreach (var item in theArray)
{
    int inLoop = item;
}
Console.WriteLine("array foreach: " + sw.Elapsed.ToString());

sw.Reset();
sw.Start();

// time for
cnt = theArray.Length;
for (int i = 0; i < cnt; i++)
{
    int inLoop = theArray[i];
}
Console.WriteLine("array for    : " + sw.Elapsed.ToString());

Console.ReadKey();

So, I ran this in release with all optimisations:

list  foreach: 00:00:00.5137506
list  for    : 00:00:00.2417709
array foreach: 00:00:00.1085653
array for    : 00:00:00.0954890

And then debug without optimisations:

list  foreach: 00:00:01.1289015
list  for    : 00:00:00.9945345
array foreach: 00:00:00.6405422
array for    : 00:00:00.4913245

So it appears fairly consistent, for is quicker than foreach and arrays are quicker than generic lists.

However, this is across 100,000,000 iterations and the difference is about .4 of a second between the fastest and slowest methods. Unless you're doing massive performance critical loops it just isn't worth worrying about.

Keith
The question was about `List<T>`, not arrays. Regardless, I don't believe that `foreach` on an native array actually uses `IEnumerable`, at least not in optimized code, so there's no real speed-up to be had. In fact, my test shows `foreach` taking only three quarters the time of `for`.
Steven Sudit
@Steven Sudit - `foreach` loops actually suffer versus `for` loops once compiler optimisation has been applied. However I did some digging around - in the case of an array `foreach` is optimised to exactly the same IL as a `for`. For a `List<T>` the compiler can't use this optimisation and the resulting IL is slightly slower. Looks like we're both wrong (._. )
Keith
@Keith: I wasn't just speaking from theory. I wrote a short test and ran it. If you'd like, I would be glad to dig it up and post it so that you can see how it works for you.
Steven Sudit
@Steven Sudit - yeah, me too. I've updated the answer with the results.
Keith
@Keith: Let me thank you for putting in the hard work, but I should mention that my tests had one key difference: I made sure the loops passed each element to an external method. Without this, the compiler is likely to optimize away the assignment entirely. I don't know if this accounts for the tiny speed boost, but it easily could.
Steven Sudit
+4  A: 

C#

myList<string>().ForEach(
    delegate(string name)
    {
        Console.WriteLine(name);
    });

Anonymous delegates are not currently implemented in VB.Net, but both C# and VB.Net should be able to do lambdas:

C#

myList<string>().ForEach(name => Console.WriteLine(name));

VB.Net

myList(Of String)().ForEach(Function(name) Console.WriteLine(name))

As Grauenwolf pointed out the above VB won't compile since the lambda doesn't return a value. A normal ForEach loop as others have suggested is probably the easiest for now, but as usual it takes a block of code to do what C# can do in one line.


Here's a trite example of why this might be useful: this gives you the ability to pass in the loop logic from another scope than where the IEnumerable exists, so you don't even have to expose it if you don't want to.

Say you have a list of relative url paths that you want to make absolute:

public IEnumerable<String> Paths(Func<String> formatter) {
    List<String> paths = new List<String>()
    {
        "/about", "/contact", "/services"
    };

    return paths.ForEach(formatter);
}

So then you could call the function this way:

var hostname = "myhost.com";
var formatter = f => String.Format("http://{0}{1}", hostname, f);
IEnumerable<String> absolutePaths = Paths(formatter);

Giving you "http://myhost.com/about", "http://myhost.com/contact" etc. Obviously there are better ways to accomplish this in this specfic example, I'm just trying to demonstrate the basic principle.

Adam Lassek
Your VB code won't work. VB only supports anonymous functions in this version, you have to wait until VB10 to have anonymous subroutines.
Jonathan Allen
You are correct, I didn't test it before posting. No wonder documentation for lambda expressions in VB are so scarce; they aren't nearly as useful.
Adam Lassek
Obvious question: Why bother with callbacks when you can just do a `foreach`?
Steven Sudit
You're assuming that the IEnumerable and delegate are from the same scope, but despite my simple example they don't have to be. Being able to pass a closure into a function from another scope can be a very powerful design pattern.
Adam Lassek
Maybe an example might help. And please start your posts to me with "@Steve" so that I get notified.
Steven Sudit
@Steve Okay I've added an example to show you what I'm talking about. Been coding Ruby for the past year, so forgive me if my C# is a bit rusty.
Adam Lassek
Thanks, Adam. With the example, I can understand what you're saying. I realize the sample code is necessarily a bit trite, but the technique does have value in the general case, particularly for the flexibility it offers if the method taking the delegate is more complex. My only concern here is that I'm not sure that it answer's the OP's question, as it's more complicated (and likely a little bit slower) than a basic foreach. I'd say it's not the best way, in general, but it's certainly one of the more flexible ways, and therefore worth knowing about on that basis.
Steven Sudit
+2  A: 

It depends on your application:

  • for loop, if efficiency is a priority
  • foreach loop or ForEach method, whichever communicates your intent more clearly
John
Why would foreach be slower?
Steven Sudit