c#: Isn't Where(), OrderBy() and Select() from Enumerable Class supposed to take a delegate type, lambda expression or anonymous type as a parameter

Viewed 86

I have got a question. See the two blocks of code. Isn't Where(), OrderBy() and Select() from IEnumerable Class supposed to take a delegate type, lambda expression or anonymous type as a parameter. If so, how did the QueryOverStringWithRawDelegate() produce the same results as QueryOverStringsWithExtensionMethods()?

void QueryOverStringsWithExtensionMethods()
{
    // Assume we have an array of strings
    string[] currentVideoGames = { "Morrowind", "Uncharted 2", "Fallout 3", "Daxter", "Bio Shock 4" };

    IEnumerable<string> subset = currentVideoGames.Where(game => game.Contains(" ")).OrderBy(game => game).Select(delegate (string game) { return game; });

    Console.WriteLine("Query Over Strings With Extension Method");
    foreach (var s in subset)
    {
        Console.WriteLine("Items: {0}", s);
    }
}

and

void QueryStringsWithRawDelegates()
{
    // Assume we have an array of strings
    string[] currentVideoGames = { "Morrowind", "Uncharted 2", "Fallout 3", "Daxter", "Bio Shock 4" };


    var subset = currentVideoGames.Where(Filter).OrderBy(ProcessItems).Select(ProcessItems);

    foreach (var s in subset)
    {
        Console.WriteLine("Items: {0}", s);
    }

    string ProcessItems(string game)
    {
        return game;
    }
    bool Filter(string game)
    {
        return game.Contains(" ");
    }
}

Thank you for your help !

2 Answers
currentVideoGames.Where(Filter)

is simply shorthand for:

currentVideoGames.Where(new Func<string, bool>(Filter))

That is, the compiler sees that you've got a method which takes a delegate type Func<string, bool>, it sees that you're giving it a method which has the signature bool Filter(string) (strictly, a method group of one or more overloads, one of which has a signature that's close enough), and it automatically inserts the code to instantiate a new delegate instance.

This same language feature lets you write things like:

SomeEvent += Handler;

rather than:

SomeEvent += new EventHandler(Handler);

See this on SharpLab.


Similarly:

currentVideoGames.Where(game => game.Contains(" "))

is shorthand for:

currentVideoGames.Where(new Func<string, bool>(CompilerGeneratedFunction))

where CompilerGeneratedFunction will look something like:

bool CompilerGeneratedFunction(string x)
{
    return x.Contains(" ");
}

See this on SharpLab. It so happens that the compiler's put CompilerGeneratedFunction (which it called <M>b__0_0) in a new inner class, and it caches the Func<string, bool> that it instantiates for performance reasons.

No, as you have observed, this is not an error. The compiler will translate method groups with the appropriate signatures into the corresponding Func<> types.

Related