(Как) можно ли привязать / повторно привязать метод для работы с делегатом другой подписи?
Я разработчик c++, который использовал сигналы и слоты в c++, которые мне кажутся аналогичными делегатам в c#. Я обнаружил, что теряюсь в поисках функциональности, обеспечиваемой "bind", и чувствую, что мне чего-то не хватает.
Мне кажется, что что-то вроде следующего, что возможно в c++, должно быть возможно в c# с делегатами. Вот несколько psudo-кодов для того, что я буду делать в c++:
Slot<void> someCallback;
int foo(int i)
{
std::cout << "Value: " << i << "n";
return i;
}
int main()
{
int i = 0;
Slot<int> someCallback = bind( fun_ptr(foo), i );
++i; // added to show that late evaluation would be a non-trivial difference
int result = someCallback();
assert( result == 0 );
return 0;
}
К сожалению, я не смог найти никаких ссылок на привязка / повторная привязка в отношении делегатов c#. Я что-то упустил? Есть ли какой-то радикально другой способ сделать это в c#?
2 ответа:
В C# мы делаем что-то вроде этого:
class Program { static Action Curry<T>(Action<T> action, T parameter) { return () => action(parameter); } static void Foo(int i) { Console.WriteLine("Value: {0}", i); } static void Main(string[] args) { Action curried = Curry(Foo, 5); curried(); } }
Очевидно, что метод
Foo
соответствует вашему методуFoo
, только с соответствующими вызовамиConsole.WriteLine
вместоstd::cout
.Далее мы объявляем метод
Curry
, который принимаетAction<T>
и возвращаетAction
. В общем случаеAction<T>
- это делегат, который принимает один параметр типаT
и возвращаетvoid
. В частности,Foo
являетсяAction<int>
, поскольку он принимает один параметр типаint
и возвращаетvoid
. Что касается возвращаемого типаCurry
, он объявлен какAction
. AnAction
- это делегат, который не имеет параметров и возвращаетvoid
.Определение
Curry
весьма интересно. Мы определяем действие, используя лямбда-выражение, которое является очень особой формой анонимного делегата. Эффективно() => action(parameter)
Говорит, что параметр
void
сопоставляется с параметромaction
, вычисляемым вparameter
.Наконец, в
Наконец, мы вызываем вновь сформированный делегатMain
мы объявляем экземплярAction
с именемcurried
, который является результатом примененияCurry
кFoo
с параметром5
. Это играет ту же роль, что иbind(fun_ptr(foo), 5)
в вашем примере C++.curried
через синтаксисcurried()
. Это похоже наsomeCallback()
в вашем примере.Причудливый термин для этого - карринг .
В качестве более интересного примера рассмотрим следующее:
class Program { static Func<TArg, TResult> Curry<TArg, TResult>( Func<TArg, TArg, TResult> func, TArg arg1 ) { return arg => func(arg1, arg); } static int Add(int x, int y) { return x + y; } static void Main(string[] args) { Func<int, int> addFive = Curry<int, int>(Add, 5); Console.WriteLine(addFive(7)); } }
Здесь мы объявляем метод
Затем в качестве теста мы объявляем методCurry
, который принимает делегат (Func<TArg, TArg, TResult>
, который принимает два параметра одного типаTArg
и возвращает a значение некоторого другого типаTResult
и параметр типаTArg
и возвращает делегат, который принимает один параметр типаTArg
и возвращает значение типаTResult
(Func<TArg, TResult>
).Add
, который принимает два параметра типаint
и возвращает параметр типаint
(aFunc<int, int, int>
). Затем вMain
мы создаем экземпляр нового делегата с именемaddFive
, который действует как метод, добавляющий пять к своему входному параметру. Таким образомConsole.WriteLine(addFive(7));
Отпечатки
12
на приставка.
Попробуйте следующее
class Example { static void foo(int i) { Console.WriteLine(i); } public static void Main() { Action someCallback = () => foo(5); someCallback(); } }
Или для чего-то еще более близкого к части счетчика C++
class Example { static void foo(int i) { Console.WriteLine(i); } static Action bind<T>(Action<T> action, T value) { return () => action(value); } public static void Main() { Action someCallback = bind(foo, 5); someCallback(); } }
Объяснение. Здесь происходит то, что я создаю нового делегата с помощью лямбда-выражения. Лямбда-это выражение, начинающееся с
() =>
. В этом случае он создает делегат, не принимающий никаких аргументов и не производящий никакого значения. Он совместим с типомAction
.