LoginSignup
19
25

More than 5 years have passed since last update.

C# .NET Frameworkでバックグラウンドワーカーに自由度を持たせる

Last updated at Posted at 2015-09-23

Visual Studioには「Background Worker」は、.NET2.0からある、いわゆる「コンポーネント」に入っているくらいの定番で、使いやすく、かんたんに非同期処理を実装するために役立つクラスです。

Microsoftの公式サイトの「方法 : バックグラウンド ワーカーを使用する」に書かれた内容通りにコードを書けば、簡単に実装できます。

ですが、ちょっと複雑なことをやろうとすると、途端に自由度がなく、やりにくくなります。

引数を渡したりとか、DoWorkイベントハンドラに、別のクラスのメソッドを仕込みたい場合などです。

DoWorkイベントハンドラから引数を渡すには?

たとえば、引数を渡したいことがあると思います。どうすれば良いでしょうか?
これには以下のようなアプローチがあります。

RunWorkerAsync()に引数を仕込む。

僕も知らなかったのですが、バックグラウンドワーカーを開始するメソッドに値を渡すことができます。

int value = 123;
backgroundWorker1.RunWorkerAsync(value); 
private void worker_DoWork(object sender, DoWorkEventArgs e) 
{
  int value = (int) e.Argument;
  //なんらかの処理

  double result = 0.1 * value;
  e.Result = result;
}

ということは、なんでも渡せるということになります。オブジェクトなど、ジェネリッククラスも引数にできます。

List<object> arguments = new List<object>();
                    arguments.Add(argument 1);
                    arguments.Add(argument 1);
                    arguments.Add(argument n);
backgroundWorker1.RunWorkerAsync(arguments);
private void worker_DoWork(object sender, DoWorkEventArgs e) {
  List<object> genericlist = e.Argument as List<object>;
}

この辺りのサンプルは、StackOverFlowで見ることができます。

メソッドごと渡す

では、ちょっとした引数ではなく、既にあるメソッドや、そこに含まれる引数も一緒に渡すにはどうすれば良いのでしょうか?

DoWorkイベントハンドラの記述に合わせず、そのままメソッドをクロージャ(ラムダ式)で渡す方法です。

backgroundWorker1.DoWork += (s, e) => MyWorkMethod(userName, targetNumber);

この正体は、以下のデリゲートメソッドでも表現できます。

backgroundWorker1.DoWork += delegate(object sender, DoWorkEventArgs e)
{
  MyWorkMethod(userName, targetNumber);
};

これも同じくStackOverFlowにあった例です。

キャンセル処理はどうすれば良いの?

ただ、メソッドを渡す方法は分かっても、キャンセル処理を、渡したメソッド内で、どうやって行えば良いのでしょうか。

すでに書いたメソッドやクラスですと、BackgoundWorkerのために、あまり内容をグチャグチャと書き換えたりしたくはないものです。

ここは僕もずいぶんとハマって、なかなかネットでも見つからなかったのですが、DoWorkイベントのObject型の「Sender」と、DoWorkEventArgs型の「e」を、そのままメソッドに渡してやることで実現できました。

backgroundWorker1.DoWork += (s, e) => MyWorkMethod(s, e, userName, targetNumber);                                       

上記の例ですと、以下の方が分かりやすいでしょうか。

backgroundWorker1.DoWork += delegate(object sender, DoWorkEventArgs e)
{
  MyWorkMethod(sender, e, userName, targetNumber);
};

メソッド内ではこのように書きます。

public bool MyWorkMethod(object sender, DoWorkEventArgs e, string userName, int targetNumber)
{
  BackgroundWorker worker = sender as BackgroundWorker;

  //なんらかの処理…

  //ユーザーによるキャンセルなど
  if ((worker.CancellationPending == true))
  {
    e.Cancel = true;
    return(false);
  }
}

至極、当たり前で、単純なやり方ですが、調べて始めてから、気づくまでに時間がかかったので、一応ここにまとめて、シェアしておきます。

19
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
25