(Jak) czy możliwe jest bindowanie / rebindowanie metody pracy z delegatem o innym podpisie?
Jestem programistą c++ używającym sygnałów i slotów w c++, co wydaje mi się analogiczne do delegatów w c#. Znalazłem się na stratę w poszukiwaniu funkcjonalności zapewnianej przez "bind" i czuję, że muszę coś przegapić.
Wydaje mi się, że coś takiego jak poniżej, co jest możliwe w c++ powinno być możliwe w c# z delegatami. Oto jakiś psudo-kod do tego, co bym zrobił w 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;
}
Niestety, nie udało mi się znaleźć żadnego odniesienia do binding/rebinding w odniesieniu do delegatów c#. Coś przeoczyłem? Czy jest jakiś radykalnie inny sposób na zrobienie tego w c#?
2 answers
W C# robimy coś takiego:
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();
}
}
Oczywiście metoda Foo
odpowiada twojej metodzie Foo
, tylko z odpowiednimi wywołaniami do Console.WriteLine
zamiast std::cout
.
Następnie deklarujemy metodę Curry
, która akceptuje Action<T>
i zwraca Action
. Ogólnie rzecz biorąc, Action<T>
Jest delegatem, który przyjmuje pojedynczy parametr typu T
i zwraca void
. W szczególności, Foo
jest Action<int>
, ponieważ przyjmuje jeden parametr typu int
i zwraca void
. Co do typu zwrotu Curry
, jest zadeklarowana jako Action
. An {[10] } Jest delegatem, który nie ma parametrów i zwraca void
.
Definicja Curry
jest dość interesująca. Definiujemy akcję za pomocą wyrażenia lambda, które jest bardzo szczególną formą anonimowego delegata. Skutecznie
() => action(parameter)
Mówi, że parametr void
jest mapowany na action
oceniany na parameter
.
Wreszcie w Main
deklarujemy instancję Action
nazwaną curried
, która jest wynikiem zastosowania Curry
do Foo
z parametrem 5
. Odgrywa to taką samą rolę jak bind(fun_ptr(foo), 5)
w twoim przykładzie C++.
Na koniec wywołujemy nowo utworzony delegat curried
poprzez składnię curried()
. To jest jak someCallback()
w twoim przykładzie.
Fantazyjne określenie na to jest currying .
Jako bardziej interesujący przykład, rozważ następujące:
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));
}
}
Tutaj deklarujemy metodę Curry
, która akceptuje delegata (Func<TArg, TArg, TResult>
, która akceptuje dwa parametry tego samego typu TArg
i zwraca wartość innego typu TResult
i parametr typu TArg
i zwraca delegata, który akceptuje pojedynczy parametr typu TArg
i zwraca wartość typu TResult
(Func<TArg, TResult>
).
Następnie, jako test deklarujemy metodę Add
, która przyjmuje dwa parametry typu int
i zwraca parametr typu int
(a Func<int, int, int>
). Następnie w {[26] } tworzymy instancję nowego delegata o nazwie addFive
, który działa jak metoda, która dodaje pięć do swojego parametru wejściowego. Tak więc
Console.WriteLine(addFive(7));
Nadruki 12
na konsola.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-20 01:06:30
Spróbuj wykonać następujące czynności
class Example {
static void foo(int i) {
Console.WriteLine(i);
}
public static void Main() {
Action someCallback = () => foo(5);
someCallback();
}
}
Lub dla czegoś jeszcze bliższego C++ counter part
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();
}
}
Wyjaśnienie. To, co się tutaj dzieje, to to, że tworzę nowego delegata za pomocą wyrażenia lambda. Lambda jest wyrażeniem zaczynającym się od () =>
. W tym przypadku tworzy delegat, który nie przyjmuje żadnych argumentów i nie generuje żadnej wartości. Jest zgodny z typem Action
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-20 00:53:48