views:

864

answers:

1

I was curious to see how the SingleOrFallback method was implemented in MoreLinq and discovered something I hadn't seen before:

    public static T SingleOrFallback<T>(this IEnumerable<T> source, Func<T> fallback)
    {
        source.ThrowIfNull("source");
        fallback.ThrowIfNull("fallback");
        using (IEnumerator<T> iterator = source.GetEnumerator())
        {
            if (!iterator.MoveNext())
            {
                return fallback();
            }
            T first = iterator.Current;
            if (iterator.MoveNext())
            {
                throw new InvalidOperationException();
            }
            return first;
        }
    }

Why is the IEnumerator<T> in a using statement? Is this something that should be thought about when using the foreach on an IEnumerable<T> also?

Side question: What does this method do exactly? Does it return the fallback item whenever the source sequence does not contain exactly one item?

+9  A: 

IEnumerator<T> extends IDisposable, so you should have it in a using statement. foreach does this automatically. (The non-generic IEnumerator doesn't extend IDisposable but the C# compiler still generates code to call Dispose conditionally. This was one of the (few) changes between C# 1.0 and 1.2, where 1.2 is the version shipping with .NET 1.1, for some reason.)

Here's an article explaining why this is important in the context of iterator blocks.

As for what the method does:

  • If the sequence is empty, return the fallback item
  • If the sequence has exactly one item, return it
  • If the sequence has more than one item, throw an exception

PS: Nice to see MoreLinq is getting some attention :)

Jon Skeet
Hehe, yeah. Added it to our project because of the MaxBy and MinBy methods. And when adding something like that, you always have to check if there is anything else you can use ;)
Svish
Not sure what this SingleOrFallback method is useful for though... could you explain what you use it for? Understand what it does now, but not when you would use such a method :p
Svish
Sure: suppose you've got a search which should return a single result, if any. You want to have some sort of default for if it doesn't return anything, but die if there's more than one result (as that indicates a bug). Basically it's SingleOrDefault but with a specified default value as a function which won't get called unless it's needed.
Jon Skeet
Aha. But SingleOrDefault contains a predicate as well, right? So to use this SingleOrFallback, you would use for example Where(predicate) and then SingleOrFallback?
Svish
SingleOrDefault has an overload with a predicate, yes. We could add one to SingleOrFallback as well if desired - but otherwise Where would do fine.
Jon Skeet