In C# 2.0 you can create objects that can be enumerated over very easily using the very Ruby-like "yield return" keyword. Here's a dumb example:
private List strings;
public IEnumerable Strings
{
get
{
if ( strings == null ) yield break;
else
foreach( string s in strings )
yield return s;
}
}
public IEnumerable
{
get
{
if ( strings == null ) yield break;
else
foreach( string s in strings )
yield return s;
}
}
The compiler will take care of all the magic of managing the state of your loops or recursion or whatever.
Pretend that the code above was in a class called ExampleStrings. I could write this code:
foreach( string s in exampleStrings.Strings ) {}
Like I said, that was a dumb example, but now you've seen an example of how easy it is to make something Enumerable. And because of how easy it is, I'm a big IEnumerable fan. I've found two cases where I really like to use it:
- When a library needs to return a bunch of "stuff" but doesn't know exactly how the consumer wants to use it
- When some non-trivial work needs to be performed to determine what the next element to be returned should be
IEnumerable strings = exampleStrings.Strings;
if ( strings == null ) return;
List l = new List( strings );
if ( strings == null ) return;
List
So maybe O(n) vs O(2n) isn't that big of a deal. And the fact that you don't have to create a List
As usual, its the second bullet that really makes taking a look at IEnumerable worth while. I've used this pattern in two actual projects too. One I referenced in an early post, Fun With Circles. The other I've used when I was manually performing some data paging.
In fun with circles I used IEnumerable to abstract away the calculation that determined the Point where the next dot in my circle should go. That way I completely separated my drawing code from the math to determine where to draw.
In the data paging example I created an IEnumerable DataFetcher. All the consumer had to do was tell the DataFetcher where to start and what direction to go in (start, end, specific record ID and forward or backward) and then simply foreach through the resulting rows. When the consumer had enough rows to fill its page, it would simply break out of the loop. If the data source ran out of data first, then the loop would end on its own. With this in place the consumer was completely isolated from when the data was being retrieved and how it was being "stitched" together. This also made it incredibly easy to add new data fetching behavior to the DataFetcher class as needed. Then by simply setting a property on the class you could change its underlying behavior without the consumer updating any other lines of code.
Of course, IEnumerable isn't required for any of this. You can accomplish the same thing with a GetNext() method. IEnumerable just adds some very nice syntactic sugar to both the consumer's code and the source's code.
hah i learned about this in the training class i took on 2.0. It is one of my favorite new things. Combine this with anonymous methods and you basically have the "code blocks" concept from ruby but strongly typed (which i could give or take).
ReplyDelete