Labels:

Prior to .NET 2.0, the only way you could create your own collection which could be traversed using the 'foreach' construct was by having you class implementing the IEnumerable and IEnumerator Interfaces.

However since .NET 2.0 we have a new operator called 'yield'. This allows your class to be iterated by its consumer but without the need to implement the above-mentioned interfaces.

Let us see a small example of how this works:


// Our class does not implement any interface !
public class School
{
// Explicit initialization just for demo purposes !
private string[] studentNames = {"John", "Joan",
"Joanne", "Tony", "Peter"};

// The iterating method
public IEnumerator GetEnumerator()
{
foreach (string name in studentNames)
{
// yield return in action !
yield return name;
}
}
}


And now the code for the consumer:


class Program
{
static void Main(string[] args)
{
School NorthCoast = new School();

// How School's internal collection was iterated is hidden to the user
foreach (string student in NorthCoast)
{
Console.WriteLine(student);
}

Console.ReadLine();
}
}


As you can see it is easy to make your class iteratable without having to make it implmenet any interfaces.

The following is another example (inspired by an e-mail sent to me by one of the readers of this blog!):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
MyYieldFileReader myYieldFileReader = new MyYieldFileReader();

foreach (string line in myYieldFileReader.ReadLine(@"C:\BuonVoyage.txt"))
{
Console.WriteLine(line);
}

Console.ReadLine();
}
}

class MyYieldFileReader
{
public IEnumerable ReadLine(string fileName)
{
using (StreamReader tr = new StreamReader(fileName))
{
while (!tr.EndOfStream)
{
yield return tr.ReadLine();
}
}
}
}
}


Finally, beware of the following constaints on the use of the 'yield' operator, as described in MSDN:


The yield statement can only appear inside an iterator block, which might be used as a body of a method, operator, or accessor. The body of such methods, operators, or accessors is controlled by the following restrictions:

Unsafe blocks are not allowed.

Parameters to the method, operator, or accessor cannot be ref or out.

A yield statement cannot appear in an anonymous method. For more information, see Anonymous Methods (C# Programming Guide).

When used with expression, a yield return statement cannot appear in a catch block or in a try block that has one or more catch clauses. For more information, see Exception Handling Statements (C# Reference).