11

私はc++でシグナルとスロットを使用したc++開発者です。これは、c#のデリゲートに類似しているようです。「バインド」によって提供される機能を探すのに迷い、何かが足りないと感じています。

c ++で可能な次のようなものは、デリゲートを使用した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#でこれを行うための根本的に異なる方法はありますか?

4

2 に答える 2

15

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対応しています。FooConsole.WriteLinestd::cout

Curry次に、を受け入れてAction<T>返すメソッドを宣言しますAction。一般に、anAction<T>は、型の単一のパラメーターを受け取り、Tを返すデリゲートですvoid。特に、Fooは、Action<int>型の1つのパラメーターを受け入れint、を返すためvoidです。の戻り型についてはCurry、として宣言されていActionます。AnActionはデリゲートであり、パラメータがなく、を返しますvoid

の定義Curryはかなり興味深いものです。匿名デリゲートの非常に特殊な形式であるラムダ式を使用してアクションを定義しています。効果的に

() => action(parameter)

パラメータがで評価voidされるようにマップされていることを示します。actionparameter

最後に、パラメータを使用してに適用した結果であるnamedMainのインスタンスを宣言しています。これは、 C++の例と同じ役割を果たします。ActioncurriedCurryFoo5bind(fun_ptr(foo), 5)

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>同じタイプの2つのパラメーターを受け入れ、TArg他のタイプの値とTResultタイプのパラメーターをTArg返し、タイプの単一のパラメーターを受け入れ、タイプTArgの値を返すデリゲートを返しますTResultFunc<TArg, TResult>)。

次に、テストとして、Add型の2つのパラメーターを受け取り、型(a )intのパラメーターを返すメソッドを宣言します。次に、入力パラメーターに5を追加するメソッドのように機能するという名前の新しいデリゲートをインスタンス化します。したがってintFunc<int, int, int>MainaddFive

Console.WriteLine(addFive(7));

12コンソールに印刷します。

于 2010-01-20T00:51:29.530 に答える
4

次を試してください

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ます。

于 2010-01-20T00:48:40.610 に答える