简体   繁体   中英

Compiler can't figure out generic types when passing a method

I'm having trouble with C# and generic type inference. I want to write a method that gets passed a method having any type, but the compiler is not able to infere the types of the method I'm passing in. The compiler always complains with with the message

Expected a method with '??? TestFunc(???, ???)' signature

Here's a testcase.

using System;

public class Example
{
    private interface ITest
    {
        int TestFunc(string str, int i);
    }

    private class Test : ITest
    {
        public int TestFunc(string str, int i) { return 0; }
    }

    public static void Main()
    {
        ITest t = new Test();
        DoWork(t.TestFunc);
    }

    public static void DoWork<T1, T2, TResult>(Func<T1, T2, TResult> func)
    {
    }
}

Can anyone explain me what's the problem?

The rules for type inference are hideously complicated, unfortunately. At least, I find them complicated, and I believe Eric and Mads are having another go at simplifying them for the next version of the spec - quite possibly not changing what's implemented, but changing how that's expressed in the spec.

In this case, the fundamental problem is that the parameter types of method groups don't contribute to type inference, even though the return type does. In particular, from 7.5.2.6 of the C# 4 spec:

Otherwise, if E is a method group and T is a delegate type or expression tree type with parameter types T1…Tk and return type Tb, and overload resolution of E with the types T1…Tk yields a single method with return type U, then a lower-bound inference is made from U to Tb.

That deals with return types, but doesn't specify anything about parameter types. The only relevant bit of the spec I can find about parameter types for method groups is this:

If E is a method group or implicitly typed anonymous function and T is a delegate type or expression tree type then all the parameter types of T are input types of E with type T.

That doesn't help to fix any bounds, unfortunately.

So basically, this works:

using System;

public class Example
{
    private interface ITest
    {
        int TestFunc();
        int TestFunc2(string value);
    }

    public static void Main()
    {
        ITest t = null;
        DoWork(t.TestFunc);
        DoWork2(t.TestFunc2);
    }

    public static void DoWork<TResult>(Func<TResult> func)
    {
    }

    public static void DoWork2<TResult>(Func<string, TResult> func)
    {
    }
}

... because the only type parameter that needs to be inferred in either case is the return type. It's when you try to infer type parameters based on the input parameters of the method that things go wrong :(

I assume the compiler does not try to infer the type in this case, since if you have overloads of TestFunc, the desired behavior is not well defined. Consider:

    private class Test 
    {
        public int TestFunc(string str, int i) { return 0; }
        public int TestFunc(string str, long i) { return 0; }
    }

    public static void Main()
    {
        Test t = new Test();
        DoWork(t.TestFunc);
    }

    public static void DoWork<T1, T2, TResult>(Func<T1, T2, TResult> func)
    {
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM