Джаред и Фог оба правы. Третий способ сделать то же самое, просто для округления, это:
Func<int, string> func = i => i.ToString();
Function<int, string> function = func.Invoke;
То есть новый делегат является делегатом метода вызова первого делегата , который имеет правильную подпись.
То, что нужно сделать, это досадно до крайности. Если бы мы сегодня проектировали среду выполнения с нуля, зная то, что мы теперь знаем о том, как люди используют типы делегатов, я думаю, вполне вероятно, что будет один встроенный тип делегата для каждой сигнатуры (так же, как и один типа "одномерный массив целых чисел"), или что среди типов делегатов будет "структурная идентичность". В следующий раз, когда вы создадите систему типов для виртуальной машины, помните об этом.