Почему C# не может вывести тип из этого, казалось бы, простого, очевидного случая


учитывая этот код:

class C
{
    C()
    {
        Test<string>(A); // fine
        Test((string a) => {}); // fine
        Test((Action<string>)A); // fine

        Test(A); // type arguments cannot be inferred from usage!
    }

    static void Test<T>(Action<T> a) { }

    void A(string _) { }
}

компилятор жалуется на то, что Test(A) Не могу понять T на string.

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

обновление 1: это в компиляторе C# 4.0. Я обнаружил проблему в VS2010, и приведенный выше пример из простейшего случая, который я сделал в LINQPad 4.

обновление 2: добавлено еще несколько примеров в список того, что работает.

5 65
c#

5 ответов:

Test(A);

это не удается, потому что единственный применимый метод (Test<T>(Action<T>)) требует вывода типа, а алгоритм вывода типа требует, чтобы каждый аргумент имел некоторый тип или был анонимной функцией. (Этот факт выводится из спецификации алгоритма вывода типа (§7.5.2)) группа методов A не имеет никакого типа (даже если он преобразуется в соответствующий тип делегата), и это не анонимная функция.

Test<string>(A);

это возможно, то разница в том, что вывод типа не требуется для привязки теста, а группа методов a преобразуется в требуемый тип параметра делегата void Action<string>(string).

Test((string a) => {});

это успешно, разница в том, что алгоритм вывода типа обеспечивает анонимные функции на первом этапе (§7.5.2.1). Параметры и возвращаемые типы анонимной функции известны, поэтому можно сделать явный вывод типа параметра, и таким образом между типами создается соответствие в анонимной функции (void ?(string)) и параметр type в типе делегата Test параметр метода (void Action<T>(T)). Для групп методов, соответствующих этому алгоритму для анонимных функций, алгоритм не задан.

Test((Action<string>)A);

это удается, разница в том, что нетипизированный метод group parameter A приводится к типу, тем самым позволяя вывод типа Test продолжать с выражением определенного типа в качестве единственного аргумента к методу.

Я не могу придумать никакой причины в теории, почему разрешение перегрузки не может быть предпринято на группе методов A. Затем-если найдена одна лучшая привязка-группе методов может быть предоставлена та же обработка, что и анонимной функции. Это особенно верно в таких случаях, когда группа методов содержит ровно один кандидат и не имеет параметров типа. Но причина, по которой он не работает в C#4, заключается в том, что эта функция не была разработана и выполненный. Учитывая сложность этой функции, ограниченность ее применения и существование трех простых обходных путей, я не собираюсь задерживать дыхание для этого!

Я думаю, это потому, что это двухэтапный вывод:

  • Он должен сделать вывод, что вы хотите преобразовать в универсальный делегат

  • Он должен определить, какой тип параметра делегата должен быть

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


Edit:

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

это похоже на порочный круг для меня.

Test метод ожидает параметр типа делегата, построенный из универсального типа Action<T>. Вы проходите в группа методов вместо: Test(A). Это означает, что компилятор должен преобразовать параметр в тип делегата (преобразование группы методов).

а какой тип делегата? Чтобы узнать тип делегата, нам нужно знать T. мы не указали его явно, поэтому компилятор должен вывести его, чтобы выяснить тип делегата.

чтобы вывести параметры типа метода, нам нужно знать типы аргументов метода, в данном случае тип делегата. Компилятор не знает тип аргумента и, таким образом, терпит неудачу.

во всех остальных случаях очевиден любой тип аргумента:

// delegate is created out of anonymous method,
// no method group conversion needed - compiler knows it's Action<string>
Test((string a) => {});

// type of argument is set explicitly
Test((Action<string>)A); 

или параметр типа указан явно:

Test<string>(A); // compiler knows what type of delegate to convert A to

П. С. подробнее о выводе типа

вы передаете имя метод А. В. Net может преобразовать его в Action, но это подразумевается, и он не будет нести за это ответственность.

но все же, имя метода не явный

Я могу ошибаться, но я полагаю, что реальная причина, по которой C# не может вывести тип, связана с перегрузкой метода и возникающей двусмысленностью. Например, предположим, что у меня есть следующие методы:void foo (int) и void foo (float). Теперь если я пишу var f = foo. Который foo если компилятор выбрать? Аналогично, та же проблема происходит с вашим примером, используя Test(foo).