Компилятор не может определить общие типы при передаче метода

У меня возникают проблемы с выводами С# и общего типа. Я хочу написать метод, который получает метод, имеющий любой тип, но компилятор не может определить типы метода, который я передаю. Компилятор всегда жалуется на сообщение

Ожидаемый метод с '??? TestFunc (???,???) '

Здесь тестовый файл.

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)
    {
    }
}

Может кто-нибудь объяснить мне, в чем проблема?

Ответ 1

Правила вывода типа ужасно сложны, к сожалению. По крайней мере, я нахожу их сложными, и я считаю, что Эрик и Мэдс еще раз решили упростить их для следующей версии спецификации - возможно, не изменив того, что было реализовано, но изменили, как это выражено в спецификации.

В этом случае основная проблема заключается в том, что типы параметров групп методов не вносят вклад в вывод типа, даже если тип возврата делает. В частности, из 7.5.2.6 спецификации С# 4:

В противном случае, если E - группа методов, а T - тип делегата или тип дерева выражений с типом параметров T1... Tk и возвращаемым типом Tb, а разрешение перегрузки E с типами T1... Tk дает один метод с возвращаемым типом U, то вывод с нижней границей выполняется от U до Tb.

Это относится к типам возвращаемых данных, но не указывает ничего о типах параметров. Единственный соответствующий бит спецификации, которую я могу найти о типах параметров для групп методов, таков:

Если E - группа методов или неявно типизированная анонимная функция, а T - тип делегата или тип дерева выражений, то все типы параметров T являются входными типами E с типом T.

Это, к сожалению, не помогает зафиксировать какие-либо границы.

Итак, в основном это работает:

с помощью 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)
    {
    }
}

... потому что единственным типом параметра, который должен быть выведен в любом случае, является тип возвращаемого значения. Это когда вы пытаетесь вывести параметры типа на основе входных параметров метода, что все идет не так: (

Ответ 2

Я предполагаю, что компилятор не пытается вывести тип в этом случае, так как если у вас есть перегрузки TestFunc, то желаемое поведение недостаточно четко определено. Рассмотрим:

    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)
    {
    }