Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

C# 2.0 Iterators and Yield Keyword – Custom Collection Enumerators

I was reading over at devlicio.us this morning and caught Derik Whittaker's article on Creating and Using Custom Collection Enumerators. After reading it, I was left with a funny feeling that we may be able to pull all that off with C# 2.0 Iterators and the Yield Keyword.

Often I avoid unnecessarily letting the client know about the details of an inner child collection class if I can get away with it, so it is not unusual for me to write something like this if it "makes sense":

 

public class Order : IEnumerable<OrderLine>
{
    private List<OrderLine> _lineItems;

    public Order()
    {
        _lineItems = new List<OrderLine>();
        _lineItems.Add(new OrderLine("Code1", 5));
        _lineItems.Add(new OrderLine("Code2", 10));
        _lineItems.Add(new OrderLine("Code3", 15));
        _lineItems.Add(new OrderLine("Code4", 20));
        _lineItems.Add(new OrderLine("Code5", 25));
        _lineItems.Add(new OrderLine("Code6", 30));

    }

    #region IEnumerable<OrderLine> Members

    public IEnumerator<OrderLine> GetEnumerator()
    {
        foreach (OrderLine lineItem in _lineItems)
        {
            yield return lineItem;
        }
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        foreach (OrderLine lineItem in _lineItems)
        {
            yield return lineItem;
        }
    }

    #endregion
}

 

By having the Order Class implement IEnumerable<OrderLine>, I now can enumerate through the line items of the order without handing over my precious _lineItems Class:

 

Order order = new Order();

foreach (OrderLine lineItem in order)
    Console.WriteLine(lineItem);
    
Console.ReadLine();

 

Obviously there are other ways to pull this off, like just having a property that returns IEnumerable<OrderLine> instead of List<OrderLine>:

 

public IEnumerable<OrderLine> LineItems
{
    get
    {
        return _lineItems;
    }
}

 

 And now you would iterate as such:

 

Order order = new Order();

foreach (OrderLine lineItem in order.LineItems)
    Console.WriteLine(lineItem);
    
Console.ReadLine();

 

The key, however, in the first example is that yield keyword, which allows us to avoid all the code you can find in Derik's post. It has all the smarts so we don't have to explicitly implement the actual members of IEnumerable<T>, IEnumerable, etc. (Current, MoveNext(), Reset(), etc.).

If we want to create a custom enumerator that perhaps only returns line items with a quantity greater than or equal to x units, we can add the following to the Order Class, which again avoids all the extra coding:

 

public IEnumerator<OrderLine> GetUnitsEnumerator(int minimumUnits)
{
    foreach (OrderLine lineItem in _lineItems)
    {
        if (lineItem.Quantity >= minimumUnits)
            yield return lineItem;
    }
}

 

 Now I use the code as such and only get the line items with 20 or more units.

 

IEnumerator<OrderLine> enumerator = order.GetUnitsEnumerator(20);

while (enumerator.MoveNext())
{
    Console.WriteLine(enumerator.Current);
}

Console.ReadLine();

 

Of course, if you could really care less about exposing the inner child collection _lineItems to the world by using the following property:

 

public List<OrderLine> LineItems
{
    get
    {
        return _lineItems;
    }
}

 

Then you have the option of just grabbing all the items with 20 or more units as such:

 

Order order = new Order();

List<OrderLine> lineItems = order.LineItems.FindAll(
    delegate(OrderLine lineItem)
        {
            return (lineItem.Quantity >= 20);
        }
);

foreach (OrderLine lineItem in lineItems)
    Console.WriteLine(lineItem);

Console.ReadLine();

 

There may be better ways to pull this off, but this can get you thinking about custom collection enumerators, etc., without diving too deep into implementing the interfaces and writing as much code. I need to spend much more time with this myself as I am sure this is just scratching the surface.

Btw, here is the OrderLine class for sake of completeness:

 

public class OrderLine
{
    private string _code;
    public string Code
    {
        get
        {
            return _code;
        }
        set
        {
            _code = value;
        }
    }

    private int _quantity;
    public int Quantity
    {
        get
        {
            return _quantity;
        }
        set
        {
            _quantity = value;
        }
    }

    public OrderLine(string code, int quantity)
    {
        _code = code;
        _quantity = quantity;
    }

    public override string ToString()
    {
        return string.Format("{0}: {1} units", _code, _quantity.ToString());
    }
}

 

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

3 Responses to C# 2.0 Iterators and Yield Keyword – Custom Collection Enumerators

  1. Hey, thanks for the full post about using Yield. I have never done it this way and will have to give it a shot.

  2. dhayden says:

    Yep! I use that one all the time, too. There are several I did not include in the post.

    That is not a custom enumerator, however, but it is a great example of passing out the enumerator of the child collection class.

  3. Geert Baeyaert says:

    Why not

    public IEnumerator GetEnumerator()
    {
    return _lineItems.GetEnumerator();
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>