David Hayden [MVP C#]

Sponsors

The Lounge

News

  • CodeBetter.Com Home

Other Links

Teas

Patterns & Practices

Florida .NET Developer

Book Reviews

Tampa ASP.NET MVC Developer Group

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
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());
    }
}

 


Posted 10-05-2006 8:43 AM by David Hayden

[Advertisement]

Comments

Geert Baeyaert wrote re: C# 2.0 Iterators and Yield Keyword - Custom Collection Enumerators
on 10-05-2006 11:16 AM

Why not

public IEnumerator<OrderLine> GetEnumerator()

{

   return _lineItems.GetEnumerator();

}

David Hayden wrote re: C# 2.0 Iterators and Yield Keyword - Custom Collection Enumerators
on 10-05-2006 1:02 PM

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.

Derik Whittaker wrote re: C# 2.0 Iterators and Yield Keyword - Custom Collection Enumerators
on 10-05-2006 7:10 PM

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

Jason Haley wrote Interesting Finds: October 5
on 10-06-2006 4:41 PM
Dan's Archive wrote New and Notable Links : Sep 27 - Oct 10 2006
on 10-11-2006 5:55 AM
Jeremy D. Miller -- The Shade Tree Developer wrote Be not afraid of the Visitor, the big, bad Composite, or their little friend Double Dispatch
on 10-31-2007 9:23 AM

Last week or so there was a thread on the altnetconf message board that about a usage of the Visitor