Tuesday 12 January 2010

My Extension Method Colllection

In addition to the small components I've shared in this blog, there's a lot of short functions that I've collected over time. Most of them are not interesting enough for a post of their own. Actually, I never seem to find a proper place to store this stuff. Until recently.

I realized that most of these functions were already coded as extension methods. And the ones that weren't extension methods, could be refactored to be. So, I started to put all of them in a single assembly called JpLabs.Extensions.

Some of this methods were extracted from sources like this question at SO, but the majority were written by me (or a co-worker) for direct use in other projects.

The full source is available to download here. Feel free to use it as you see fit. All feedback is welcome.

Thursday 7 January 2010

Iterators and Argument Validation

Iterators were introduced in C# 2.0, so they are not new. Before LINQ was born, I think most C# programmers had never seen an yield operator. I'm not sure when I saw it for the first time, but I certainly wasn't an early user. I still remember the puzzlement I felt trying to understand how this stuff works.

If you ever opened the Enumerable class in Reflector you might have noticed that it has several nested private compiler-generated classes. This nested classes are generated whenever a class have iterator functions.

The point I want to make in this post is about argument validation. I've seen in more than once people making a simplification that, IMHO, should be considered a defect in production code. As an example, consider the code below (which I extracted from an answer in stackoverflow).

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector
) {
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
if (selector == null) throw new ArgumentNullException("selector");

using (var enum1 = first.GetEnumerator())
using (var enum2 = second.GetEnumerator())
while (enum1.MoveNext() && enum2.MoveNext())
yield return selector(enum1.Current, enum2.Current);
}

It's quite simple and it might be hard to find any issues even after reading it carefully. It will even pass through positive testing. The problem only appears when you try to send a null value to any of the three parameters. As you may know, the code of an iterator function is not executed right after the call. It's executed only when the IEnumerable is enumerated. This behavior is called delayed execution. Because of that, when a null argument is passed, the ArgumentNullException won't be thrown until the enumeration of the returned value. This is highly undesirable.

Fortunately, there's a very simple way to avoid the problem. As mentioned in MSDN community content in this page, you should break the iterator function in two functions. Like this:
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector
) {
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
if (selector == null) throw new ArgumentNullException("selector");

return Zip(first, second, selector);
}

private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(
IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector
) {
using (var enum1 = first.GetEnumerator())
using (var enum2 = second.GetEnumerator())
while (enum1.MoveNext() && enum2.MoveNext())
yield return selector(enum1.Current, enum2.Current);
}

I highlighted the key points. Notice that only the second function is an iterator, and it's private. The only place where it should be called is in the first function.

By the way, Zip is an extension method added in .NET 4.0. If you want to read more about Zip and the elusive behavior of exceptions in iterator functions, I recommend this two posts:

- Bart's LINQ's new Zip operator at http://community.bartdesmet.net/blogs/bart/archive/2008/11/03/c-4-0-feature-focus-part-3-intermezzo-linq-s-new-zip-operator.aspx;

- Eric's Zip Me Up at http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx;

- Eric's High maintenance at http://blogs.msdn.com/ericlippert/archive/2008/09/08/high-maintenance.aspx.