(Как) можно ли привязать / перепривязать метод для работы с делегатом другой подписи? - PullRequest
11 голосов
/ 20 января 2010

Я - разработчик на С ++, использующий сигналы и слоты на С ++, который мне кажется аналогичным делегатам на С #.Я оказался в растерянности в поиске функциональности, предоставляемой «bind», и чувствую, что должен что-то упустить.

Мне кажется, что что-то вроде следующего, что возможно в c ++, должно быть возможнымв с # с делегатами.Вот некоторый псевдокод для того, что я сделал бы в 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 ]

15 голосов
/ 20 января 2010

В 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.Action является делегатом, который не имеет параметров и возвращает void.

Определение Curry довольно интересно.Мы определяем действие, используя лямбда-выражение, которое представляет собой особую форму анонимного делегата.Фактически

() => action(parameter)

говорит, что параметр void сопоставляется с action, оцененным в parameter.

Наконец, в Main мы объявляем экземпляр Actionс именем curried, который является результатом применения Curry к Foo с параметром 5.Это играет ту же роль, что и bind(fun_ptr(foo), 5) в вашем примере C ++.

Наконец, мы вызываем вновь сформированный делегат curried через синтаксис curried().Это похоже на someCallback() в вашем примере.

Причудливый термин для этого: curry .

В качестве более интересного примера рассмотрим следующее:

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 и возвращает значение некоторого другого типа TResult и параметр типа TArg и возвращает делегата, который принимает один параметр типа TArg и возвращает значение типа TResult (Func<TArg, TResult>).

Затем в качестве теста мы объявляем метод Add, которыйпринимает два параметра типа int и возвращает параметр типа int (a Func<int, int, int>). Затем в Main мы создаем новый делегат с именем addFive, который действует как метод, который добавляет пять к своему входному параметруТаким образом,

Console.WriteLine(addFive(7));

печатает 12 на консоли.

4 голосов
/ 20 января 2010

Попробуйте следующее

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.

...