web-dev-qa-db-ja.com

.NET Actionを使用して、不明な数のパラメーターでメソッドを実行する方法は?

進行状況バーをユーザーに表示しながら、ワーカースレッドでいくつかの操作を実行したいと思います。クラスを作成しました

_public class ProgressBar
{
   public void StartAsyncTask(Action action)
   {
      Task t = new Task(action);
      t.start();
   }
}
_

次の方法でStartAsyncTaskに任意のメソッドを送信できることがわかりました。

_  ProgressBar pb = new ProgressBar();
  pb.StartAsyncTask( () => DoSomething(15, "something"));

  public void DoSomething(int i, string s)
   {
      //do something
   }
_

まず第一に、ラムダ式-_() =>_-が何であり、どのように翻訳され、Actionオブジェクトがどのように不明な数のパラメーターを持つデリゲートに渡されるのかを理解できないようです。

ProgressBarでBackgroundWorkerを使用したいのですが、この場合はアクションを呼び出す必要があります。このようなもの:

_void m_backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    Action action = e.Argument as Action; //same action here passed through EventArgs
   //but already in a worker thread so no need for the Task object 

   //and now i need to somehow invoke the action object but i don't know what the parameters are. 

   action.Invoke( ? ); 
}
_

最初の例で、StartAsyncTask(Action action)メソッドのパラメーターを知らなくてもアクションを実行する方法はありますか?

この場合、アクションを呼び出すときにパラメーターを知る必要があるのはなぜですか?

"Action"を使用する方法/理由/タイミングに関するすべては、MSDNのドキュメントや他のスレッドをここで読んだとしても、私にはかなり明確ではありません。これに関する情報は私に役立ちます。

22
Dan Dinu

あなたは物事を少し考え過ぎていると思います。それでは、上から始めましょう:

  1. ラムダ式は、メソッドの実行を参照する表記法です。例:

    _ x => x + 3
    _

    最も基本的なレベルでは、これは1つの入力xを受け取り、_x + 3_に等しい値を返す関数を表します。あなたの状況では、あなたの表現:

    _() => DoSomething(15, "Something")
    _

    0個のパラメーターを受け取り、メソッドDoSomething(15, "Something")を呼び出すメソッドを表します。コンパイラは、それをFuncまたはActionデリゲートに変換する舞台裏にいます。したがって、それは有効です:

    _new Action(delegate()
    {
        DoSomething(15, "Something")
    }); 
    _

    上記の単純な式のコンパイラの書き換えは次のようになります。

    _new Func<int, int>(delegate(int x)
    {
        return x + 3;
    });
    _
  2. 次に、アクションを後で呼び出したい場合、そうするための構文はかなり簡単です:

    _Action someMethod = new Action(() => { Console.WriteLine("hello world"); }));
    someMethod(); // Invokes the delegate
    _

    したがって、Actionインスタンスは、Actionが0パラメーターを受け取り、何も返さないデリゲートであるため、_()_構文を使用して呼び出すだけで十分です。

    関数も同様に簡単です。

    _Func<int, int> previousGuy = x => x + 3;
    var result = previousGuy(3); // result is 6
    _
  3. 最後に、呼び出すメソッドを渡して、その時点でパラメーターのコンテキストがない場合、呼び出しをアクションにラップして、後で呼び出すことができます。例えば:

    _var myAction = new Action(() =>
         {
              // Some Complex Logic
              DoSomething(15, "Something");
              // More Complex Logic, etc
         });
    
    InvokeLater(myAction);
    
    public void InvokeLater(Action action)
    {
          action();
    }
    _

    すべてのデータはメソッドのクロージャーでキャプチャされるため、保存されます。したがって、e.Argument_プロパティを使用してActionをイベントに渡すことができた場合は、_(e.Argument as Action)()_を呼び出すだけで十分です。

50
Tejs

そのデリゲートでDynamicInvoke()を使用することはできません(params object[] args引数として)

action.DynamicInvoke(arg1, arg2, arg3 );
2
Hulvej