1
votes

I have been playing with type inference in C# and I have read this thread. As stated in the thread, type inference does not work for generic delegates (when you pass a method to this delegate parameter without any explicit casting) like Func, Action and etc. Then I would like to try LINQ operators like SelectMany with .net Func classes instead of lambda expressions in order to see the result. However, it was not result I expected.

I created a test class named L1 which contains a list of strings in order to use (flatten) in SelectMany. In addition to that I created a test method in order to pass instead of Func type parameter.

public class L1
{
    private L1() {}

    public L1(string m)
    {
        Isim = m;
        numbers = new List<string> { "1", "2", "3", "4", "5" };
    }

    public List<string> numbers; 
    public string Isim { get; private set; }
}
private static IEnumerable<int> a(L1 k)
{
    return Enumerable.Empty<int>();
}

When I created an extension method like the one below, C# compiler cannot inference the type of the generic parameters.

public static void Test<T1, T2>(this L1 l, Func<T1, IEnumerable<T2>> a)
{
}

(new L1("test")).Test(a);

It gives 'error CS0411: The type arguments for method 'EnumSand.Test(L1, Func>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.' as I expexted.

However, when I use this method a in LINQ SelectMany extension method, compiler does not give any error and it works.

L1[] l1 = new L1[5] {
                        new L1("one"), new L1("two"), 
                        new L1("three"), new L1("four"), 
                        new L1("five")
                    };

var slack = l1.SelectMany(a);

foreach (var t in slack)
{
    Console.WriteLine(t);
}

So, what is the difference between these cases and how it is possible in SelectMany to make a good type inference?

1

1 Answers

2
votes

The problem is here:

public static void Test<T1, T2>(this L1 l, Func<T1, IEnumerable<T2>> a)

T2 is bound to int but T1 may be anything, that's why it cannot be inferred.

You can either change second parameter to Func<L1, IEnmuerable<T2>> and remove T1 completely OR change this L1 to this T1.

EDIT: consider the situation when you have following methods:

private static IEnumerable<int> a(L1 k)
{
    return Enumerable.Empty<int>();
}

private static IEnumerable<int> a(string k)
{
    return Enumerable.Empty<int>();
}

Can you now say which a method should be used for (new L1("test")).Test(a); use?