Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can anyone explain this enumerator syntax?

Tags:

c#

.net

morelinq

public static IEnumerable<T> Pipe<T>(this IEnumerable<T> source, Action<T> action)
{    
    return _(); IEnumerable <T> _()
    {
        foreach (var element in source)
        {
            action(element);
            yield return element;
        }
    }
}

I've found this code in MoreLinq repo and can't understand this line:

return _(); IEnumerable <T> _()
like image 301
Bohdan Other Avatar asked Jan 04 '23 00:01

Bohdan Other


1 Answers

This code uses a relatively new feature of C#, called local function. The only unusual thing about this function is its name: developers used a single underscore for it. Hence, the name of the function is _, so the invocation looks like this: _()

Now that you know that return statement returns the result of invoking a local function named _, the rest of the syntax falls into place:

// This is a local function
IEnumerable <T> _() {
    ...
}

(OP's comment on the question) Can't we just do foreach with yield return?

The method that you copied included two additional lines, which are key to understanding the difference:

public static IEnumerable<T> Pipe<T>(this IEnumerable<T> source, Action<T> action)
{    
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (action == null) throw new ArgumentNullException(nameof(action));
    return _(); IEnumerable <T> _()
    {
        foreach (var element in source)
        {
            action(element);
            yield return element;
        }
    }
}

If you put foreach with yield return directly into the body of Pipe<T> method, argument checking would be deferred until you start iterating your IEnumerable<T> result. With local function in place you would do the check as soon as Pipe<T> is called, even in situations when the caller never iterates the result.

like image 165
Sergey Kalinichenko Avatar answered Jan 12 '23 09:01

Sergey Kalinichenko