Вместо того, чтобы атаковать вашу проблему напрямую, которая требует некоторого разъяснения, позвольте мне обратиться к вашей проблеме здесь:
Я где-то читал, что число параметров должно быть> = количество типов шаблонов
Давайте немного разберёмся с этим.
Прежде всего, это generi c параметры типа , а не "типы шаблонов". Это не C ++. Дженерики похожи на шаблоны, но они , а не шаблоны, и чем раньше вы перестанете думать о них как о шаблонах, тем лучше.
Неверно, что число формальных параметров должно быть больше или равно количеству параметров типа generi c, объявленных методом generi c. Например:
static void M<K, V>(Dictionary<K, V> d) { }
...
Dictionary<int, string> d = whatever;
M(d); // No problem!
Число формальных параметров равно единице, число параметров типа generi c равно двум, но у нас нет проблем с выводом типа здесь.
Реальное Правило немного сложнее. Скорее, настоящая проблема, с которой вы сталкиваетесь:
Преобразование группы методов в делегат требует, чтобы типы параметров делегата были известны до преобразования .
Предположим, у нас есть:
int F(string x) { ... }
void M<A, R>(Func<A, R> f) { ... }
M(F);
Что происходит? Мы должны определить, что означает F при преобразовании в Func<A, R>
, но мы не знаем ни A
, ни R
. Как мы определяем значение? У нас разрешение перегрузки . То есть мы притворимся, что был вызов:
A a = whatever;
F(a)
и спросили «какой метод с именем F будет работать?»
Но мы никогда даже не доберемся до этого шага, потому что мы еще не знаем, что такое A
. Вывод типа не может прогрессировать. Теперь, если, напротив, у вас было:
int F(string x) { ... }
void M<A, R>(A a, Func<A, R> f) { ... }
M("abc", F);
Теперь вывод типа сначала говорит: «Я использую "abc"
для a
, что A
равно string
». После того, как сделан этот вывод, теперь разрешение перегрузки будет успешным Если бы мы сделали
string a = whatever;
F(a);
, то разрешение перегрузки определило бы, что F
означает int F(string)
.
Как только мы определили, что F
означает int F(string)
, теперь мы можем задать вопрос: «Что мы можем сделать вывод о преобразовании из int F(string)
в Func<string, R>
и из чего мы выводим R
должно быть int
и мы закончили.
Я знаю, что вы спросите дальше. У меня есть только одна перегрузка с именем F, так почему бы нам просто не выбрать ее автоматически?
Существует много проблем с созданием подобных исключений. Во-первых, особые случаи имеют тенденцию к умножению, и вскоре у нас есть еще более безумный алгоритм вывода, который никто не понимает, и его нельзя изменить, не вызывая ошибок. , это делает ваш код хрупким, это означает, что логический вывод зависит от числа доступных методов с именем F в области действия . Предположим, вы добавили новый закрытый метод, также называемый F
; внезапно ли логический вывод изменится?
Нет, правило простое и понятное, если вы его знаете. Группы методов разрешаются точно так же, как если бы был вызов метода . Но w Мы не можем имитировать вызов до тех пор, пока после не будут выведены типы аргументов.
Поверьте, я, как и любой другой, знаю, насколько хитрым может быть алгоритм вывода типов в C #; у него много таких удивительных случаев. Если у вас есть более четкий вопрос о разработке, реализации или спецификации этого алгоритма, не стесняйтесь, чтобы открыть новый вопрос и оставить мне комментарий к этому ответу, и я постараюсь посмотреть.