Click here to Skip to main content
Click here to Skip to main content

IEnumerable<> and Yield in .NET 2.0

, 11 Jun 2007
Rate this:
Please Sign up or sign in to vote.
Using IEnumerable and Yield to create on-the-fly collections in .NET 2.0

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

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-generic IEnumerable and IEnumerator interfaces. 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. Using yeilds it's also easier to create custom objects that can be iterated which will be the topic of my next article.

History

  • 9 June, 2007 -- Added program output and a few gramar corrections
  • 8 June, 2007 -- Original version posted

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Ryan Andrus
Software Developer (Senior)
United States United States
I live with my beautiful wife Heather in Houston, Texas and currently work at Input\Output. I am also available for afterhours consulting, so if you have a need then email me.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberHaribabu1234-Aug-10 23:48 
GeneralNice Article PinmemberDaniel Ballinger21-Jun-07 11:41 
QuestionWhen it is a bit more complicated... PinmemberUrs Enzler18-Jun-07 21:15 
AnswerRe: When it is a bit more complicated... PinmemberLuc Pattyn20-Jun-07 14:28 
GeneralRe: When it is a bit more complicated... PinmemberUrs Enzler20-Jun-07 23:54 
GeneralNot just more comfortable PinmemberLuc Pattyn9-Jun-07 6:20 
NewsError in sample code! PinmemberRyan Andrus9-Jun-07 3:17 
GeneralGreat Pinmembermerlin9818-Jun-07 10:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141216.1 | Last Updated 11 Jun 2007
Article Copyright 2007 by Ryan Andrus
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid