10

Having these basic definitions

bool MyFunc(string input)
{
    return false;
}
var strings = new[] {"aaa", "123"};

I'm wondering why this won't compile :

var b = strings.Select(MyFunc);

But this will:

var c = strings.Select(elem => MyFunc(elem));

The error message is "The type arguments for method 'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage."

The Resharper error tip says it's confused between

Select(this IEnumerable<string>, Func<string, TResult>)

and

Select(this IEnumerable<string>, Func<string, int, TResult>)

...but the signature for MyFunc is clear - it just takes one (string) parameter.

Can anyone shed some light here?

Cristian Diaconescu
  • 34,633
  • 32
  • 143
  • 233

2 Answers2

14

Generic type inference changed slightly - in terms of implementation - between the C# 3 and C# 4 compiler. Here's a short but complete example program:

using System;
using System.Linq;

class Test
{
    static void Main()
    {
        string[] strings = { "a", "b" };
        var results = strings.Select(MyFunc);
    }

    static bool MyFunc(string input)
    {
        return true;
    }
}

That compiles with the C# compiler in .NET 4, but not the one in .NET 3.5.

I think it's reasonable to call this a bug fix, as I don't think it was a spec change.

If you have to use the compiler from .NET 3.5, you can add a cast to clarify:

var results = strings.Select((Func<string,bool>) MyFunc);

or

var results = strings.Select(new Func<string,bool>(MyFunc));

or you could make the type argument explicit:

var results = strings.Select<string, bool>(MyFunc);
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • So it *wasn't* unreasonable of me to think the compiler would figure things out... – Cristian Diaconescu Mar 09 '11 at 12:52
  • @Cristi: Nope... although trying to discern from the spec exactly how it should work is tricky. I think of the type inference part of the spec as having "Here be dragons" written all over it. – Jon Skeet Mar 09 '11 at 12:54
  • We decided to change the behaviour when it turned out to be too late to get the change into C# 3, so we put it in C# 4 instead. – Eric Lippert Mar 09 '11 at 15:25
  • (And Mads and I are probably going to rewrite the type inference spec *again* because as it stands now it is not correct with respect to named/optional parameters. We'll try to make it easier to follow.) – Eric Lippert Mar 09 '11 at 15:26
  • 1
    @Eric: If you're interested in beta testers for reading the new spec, I'm sure you know I'd be happy to help :) – Jon Skeet Mar 09 '11 at 15:28
  • Any reason you prefer to cast the method-group to the delegate-type rather than just specify the type arguments for the method? `strings.Select(MyFunc)`. – Ani Mar 09 '11 at 20:57
  • @Ani: Not particularly... will add that as an option. – Jon Skeet Mar 09 '11 at 20:59
  • On a related note, would you mind taking a look at [this](http://stackoverflow.com/questions/5203792/overloaded-method-group-argument-confuses-overload-resolution)? Thanks a lot. – Ani Mar 09 '11 at 20:59
6

Jon is of course correct as usual. Some additional information:

Here's the blog article from 2007 where I describe the problem you're having:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

Based on the feedback on that article we decided to fix this, but could not get the fix into C# 3 for scheduling reasons.

A few months after that I announced that the fix would go into C# 4, not into the C# 3 service pack:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/28/method-type-inference-changes-part-zero.aspx

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067