make метод в C#, который принимает функцию без параметров типа - PullRequest
4 голосов
/ 23 апреля 2020

Моя конечная цель состоит в том, чтобы получить функцию, которая может принимать любую другую функцию - например, что-то вроде

      void RegisterHandler( Delegate function);

- я хочу иметь возможность вызывать ее без выполнения приведения или без параметров типа - очевидно, делегат не работает. Если я заявляю:

      void RegisterHandler<T>( Func<T> function );

, тогда он работает нормально - это работает:

      string Whatever() {return "hi";}
      ...
      RegisterHandler( whatever );

однако - как только я добавляю параметры, он не работает

      void RegisterHandler<TParam1,TReturn>( Func<TParam1, TReturn> function );
      string Something( int x ) {return x.ToString();}
      ...
      RegisterHandler( Something );               //  doesn't compile - wants types specified
      RegisterHandler<int,string>( Something );   // works, but is what I'm trying to avoid

Я бы даже был в порядке, если бы «объект» работал - это всего лишь часть свободного интерфейса, и я просто хочу иметь возможность вызывать его. Я где-то читал, что число параметров должно быть> = количество типов шаблонов, и я предполагаю, что возвращаемый тип считается, поэтому я никогда не смогу выполнить это.

Нижняя строка - я не могу найти любая c# подпись, которая будет принимать любую функцию, которая принимает один параметр и возвращает что-то. Возможно ли это?

Ответы [ 2 ]

13 голосов
/ 23 апреля 2020

Вместо того, чтобы атаковать вашу проблему напрямую, которая требует некоторого разъяснения, позвольте мне обратиться к вашей проблеме здесь:

Я где-то читал, что число параметров должно быть> = количество типов шаблонов

Давайте немного разберёмся с этим.

Прежде всего, это 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 #; у него много таких удивительных случаев. Если у вас есть более четкий вопрос о разработке, реализации или спецификации этого алгоритма, не стесняйтесь, чтобы открыть новый вопрос и оставить мне комментарий к этому ответу, и я постараюсь посмотреть.

1 голос
/ 23 апреля 2020

Вы ... ищете Action?

Func<T> - это делегат. net, предназначенный для возврата значения.

Действие - это. net делегат, который не возвращает значение

Action<T> также принимает до 16 параметров.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...