Указывая x.MethodB
, вы фактически не предоставляете делегата напрямую, а, скорее, так называемую группу методов .Группы методов могут неявно преобразовываться в данный совместимый тип делегата .
Таким образом, компилятор сталкивается со следующей ситуацией при попытке вывести аргумент универсального типа T1
вашего AddMethod<T1>
метода.Чтобы иметь возможность вывести T1
, компилятор должен знать конкретный тип делегата Action<T1>
, который у вас под рукой.Но все, что дано компилятору, это группа методов, а не Action<double>
делегат.
Чтобы иметь возможность (неявно) преобразовать группу методов в делегат, компилятору необходимо знать совместимый тип делегатагруппа методов должна быть преобразована в.Но компилятор мог бы знать этот тип делегата, только если бы он мог вывести T1
, что опять-таки потребовало бы, чтобы он знал конкретный тип Action<T1>
.Catch-22.
Аналогичная, но более простая проблема, иллюстрирующая ту же самую проблему:
static void SomeMethod<T1>(Action<T1> action) { }
var sc = new SomeClass();
SomeMethod(sc.MethodB); // compile error, type argument cannot be inferred
Здесь компилятор также не сможет вывести SomeMethod's введите параметр T1
, поскольку ему присваивается группа методов, а не делегат Action<T1>
.И он не может преуспеть в преобразовании группы методов в делегат, потому что для определения фактического конкретного типа Action<T1>
для преобразования ему потребуется конкретный тип Action<T1>
для вывода T1
из.
Ниже небольшой (глупый и непрактичный) пример, показывающий, что проблема не в выводе самого T1
, а в отсутствии конкретного конкретного типа делегата Action<T1>
, из которого можно вывести T1
.Здесь, в лямбда-выражении дается только делегат Action<double>
, что, в свою очередь, позволит компилятору выводить T1
:
var sc = new SomeClass();
Action<double> d = sc.MethodB;
builder.AddMethod(x => d); // Gives no error anymore
или, несколько менее глупо:
builder.AddMethod(x => (Action<double>) x.MethodB); // Gives no error anymore