Throw Before You Yield
In a comment left by Bart De Smet, he pointed out that I failed to address the fact that the execution of all "yielding" methods is deferred.
For instance, when running the following code, no exceptions will be thrown:
int[] arr = null;
var copy = arr.Enumerate();
// ...
static IEnumerable<T> Enumerate<T>(this IEnumerable<T> enumeration)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");
foreach (var item in enumeration)
{
yield return item;
}
}
This is because the first time the code from the method will run will be after there a call to copy's GetEnumerator(), followed by a call to that object's MoveNext() method has been made (i.e. when we enumerate over copy).
To overcome this problem, we'll change the code slightly:
static IEnumerable<T> Enumerate<T>(this IEnumerable<T> enumeration)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");
return EnumerateCore(enumeration);
}
private static IEnumerable<T> EnumerateCore<T>(IEnumerable<T> enumeration)
{
foreach (var item in enumeration)
{
yield return item;
}
}
Now a NullReferenceException will immediately be thrown out of Enumerate, since the "unyielding" method will first run the code and then defer-call to the "yielding" method. This helps our "yielding" methods adhere to the principal of fail-fast.