Introduction

IEnumerable is an interface that has allowed for the creation of collections which can be iterated with a foreach loop since .NET 1.0. Well, with the introduction of generics and the yield keyword in .NET 2.0 things have gotten a whole lot simpler. This article will demonstrate how to use the generic IEnumerable<> interface along with the new yield keyword to create collections on the fly, without the need to implement all of the boiler plate code that was previously required.

Background

Before .NET 2.0, in order to create a collection that could be iterated using foreach you had to implement a class that implemented IEnumerable . This class would then have to implement IEnumerable.GetEnumerator() , which would have to return an object that implements IEnumerator . The developer would also have to create the class that is returned by IEnumerable.GetEnumerator() . This class would have to implement three methods from the IEnumerator interface. So a developer would have to create two custom classes and implement two interfaces and four methods in those two new classes.

As you can see, there is a fair amount of code that goes into creating a collection this way. This article is not going to cover the old way of doing things, as you can easily find a lot of resources on the web and MSDN that explain that process. Rather, this article will cover how to use IEnumerable<> in conjunction with the new yield keyword to create methods that return IEnumerable collections in way less code than was previously possible.

The code

This code has a method called Helpers.GetIntCollectionFromString() . It takes a string and returns an IEnumerable<int> that can be iterated in a foreach loop.

using System; using System.Collections; using System.Collections.Generic; class Helpers { public static IEnumerable<int> GetIntCollectionFromString( string stringToScan) { Console.WriteLine( " Preparing to scan string" ); string [] tokens = stringToScan.Split( ' ' ); int intToAdd; foreach ( string token in tokens) { if ( int .TryParse(token, out intToAdd)) { yield return intToAdd; } else { yield break ; } } } } class foo { static void Main() { string goodTestData = " 1 2 3 4 5 6 7" ; foreach ( int listItem in Helpers.GetIntCollectionFromString( goodTestData)) Console.WriteLine(listItem); string badTestData = " 1 2 3 afdf 4 5 6" ; foreach ( int listItem in Helpers.GetIntCollectionFromString( badTestData)) Console.WriteLine(listItem); Console.Read(); } }

How it works

As you can see in the code below...

public static IEnumerable<int> GetIntCollectionFromString( string stringToScan) { Console.WriteLine( " Preparing to scan string" ); string [] tokens = stringToScan.Split( ' ' ); int intToAdd; foreach ( string token in tokens) { if ( int .TryParse(token, out intToAdd)) { yield return intToAdd; } else { yield break ; } } }

...I have created a public static method that returns IEnumerable<int> . This is the type that is needed to iterate a collection using foreach loops. Then in Helpers.GetIntCollectionFromString() each time I want to add an int to the collection that will be returned, I use the new yield keyword as show below:

yield return intToAdd;

When yield is used in this way with return , it acts differently from just using return by itself. When return is used by itself, the function returns and no more processing is performed. However, when it is used in conjunction with yield , return instead indicates to the runtime to add the value that would normally be returned to the collection that is to be returned, but to keep processing the function. So, in my code example, as long as there are items to be processed by...

foreach ( string token in tokens)

...the function will not return. Instead, it will just keep telling the runtime via yield return to add each item processed to the IEnumerable<int> collection that will be returned when there is nothing left to process.

If you have been reviewing my code, by now you might be wondering what yield break does. yield break allows you to indicate that you are done processing and to return from the function. So, in my sample code, whenever a string that can't be converted to an int is found, processing will stop and an IEnumerable<int> collection will be returned with all items that were added via yield return .

When you run my code you will get the output shown bellow:

Preparing to scan string 1 2 3 4 5 6 7 Preparing to scan string 1 2 3

Final Thoughts

IEnumerable

IEnumerator

yeilds

History

9 June, 2007 -- Added program output and a few gramar corrections

8 June, 2007 -- Original version posted

I think this is a really handy new tool that allows for developers to quickly create functions that return custom collections without having to go through all the pain of implementing the non-genericandinterfaces. I could easily imagine that you would find yourself using these types of collections a whole lot more often now that they have simplified their creation. Usingit's also easier to create custom objects that can be iterated which will be the topic of my next article.