Water Cooler v2
Water Cooler v2

Reputation: 33850

Why is DefaultIfEmpty implemented this way?

Chasing the implementation of System.Linq.Enumerable.DefaultIfEmpty took me to this method. It looks alright except for the following quaint details:

// System.Linq.Enumerable
[IteratorStateMachine(typeof(Enumerable.<DefaultIfEmptyIterator>d__90<>))]
private static IEnumerable<TSource> DefaultIfEmptyIterator<TSource>(IEnumerable<TSource> source, TSource defaultValue)
{
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
        {
            do
            {
                yield return enumerator.Current;
            }
            while (enumerator.MoveNext());
        }
        else
        {
            yield return defaultValue;
        }
    }
    IEnumerator<TSource> enumerator = null;
    yield break;
    yield break;
}

1) Why does the code have to iterate over the whole sequence once it has been established that the sequence is not empty?

2) Why the yield break two times at the end?

3) Why explicitly set the enumerator to null at the end when there is no other reference to it?

I would have left it at this:

// System.Linq.Enumerable
[IteratorStateMachine(typeof(Enumerable.<DefaultIfEmptyIterator>d__90<>))]
private static IEnumerable<TSource> DefaultIfEmptyIterator<TSource>(IEnumerable<TSource> source, TSource defaultValue)
{
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
        {
            do
            {
                yield return enumerator.Current;
            }
            // while (enumerator.MoveNext());
        }
        else
        {
            yield return defaultValue;
        }
    }
    // IEnumerator<TSource> enumerator = null;
    yield break;
    // yield break;
}

Upvotes: 2

Views: 1288

Answers (3)

Arturo Menchaca
Arturo Menchaca

Reputation: 15982

Why does the code have to iterate over the whole sequence once it has been established that the sequence is not empty?

As you can read in MSDN about DefaultIfEmtpy return value:

An IEnumerable<T> object that contains the default value for the TSource type if source is empty; otherwise, source.

So, if the enumerable is empty the result is a enumerable containing the default value, but if the enumerable isn't empty the same enumerable is returned (not only the first element).

It may seem that this method is about checking only whether an enumerable contains elements or not, but it is not the case.

Why the yield break two times at the end?

No ideas :)

Upvotes: 1

Yacoub Massad
Yacoub Massad

Reputation: 27861

DefaultIfEmpty needs to act as the following:

  1. If the source enumerable has no entries, it needs to act as an enumerable with a single value; the default value.

  2. If the source enumerable is not empty, it needs to act as the source enumerable. Therefore, it needs to yield all values.

Upvotes: 2

Sami Kuhmonen
Sami Kuhmonen

Reputation: 31143

Because when you start enumerating and this code is used as another level of enumeration you have to enumerate the whole thing.

If you just yield return the first one and stop there the code using this enumerator will think there is only one value. So you have to enumerate everything there is and yield return it forward.

You could of course do return enumerator and that would work, but not after the MoveNext() has been called since that would cause the first value to be skipped. If there was another way to check if values exist then this would be the way to do it.

Upvotes: 1

Related Questions