(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#?

Author: Catskul, 2010-01-20

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.

 14
Author: jason,
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.

 4
Author: JaredPar,
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