非同期デリゲートからGUIにアクセスする場合のイディオム

.NET2.0では、Windowsフォーム内のコントロール(System.Windows.Forms.Controlクラスの派生クラス)は、特定のスレッドにバインドされているため、非同期デリゲートなどを経由して他のスレッドからアクセスすると、間違いなく例外が発生する。

System.InvalidOperationException はハンドルされませんでした。
Message="有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール '' がアクセスされました。"

これを避けるためには、Microsoftの言葉を借りると「コントロールの invoke メソッドを使用して、適切なスレッドへの呼び出しをマーシャリングする必要があります。」となるのだが、いまひとつピンとこないし、勝手が良くない。

よく使うパターンとして、Control派生クラスにアクセスするロジックを別メソッドとして分離し、そのメソッドと同じ型のデリゲートを用意して、以下のように記述することで例外を防げる。

//メソッドと同じシグネチャを持つデリゲートの定義が必要
private delegate void ResultShowingHandler(object args);

//Controlにアクセスするロジックを切り出したメソッド
private void ResultShowing(object args)
{
    //非同期処理に対応
    if (this.Form1.InvokeRequired)
    {
        ResultShowingHandler handler = new ResultShowingHandler(this.ResultShowing);
        this.Form1.Invoke(handler, new object[] {args});
        return;
    }
    //Formとその配下のコントロールにアクセスする処理を記述
}

もっと良いパターンはあると思うし、ベタだけど自分用なんで。

いつもそうなんだけど、.NETでの開発を暫くしないでいると、一番先に忘れてしまうのがデリゲートと、特に非同期デリゲート。何度プログラミングしても、何度となく非同期デリゲートのパターンで書いても、半年も放置しておくとすっからかんに忘れてしまう。自分では、記述の仕方や構成が直感的ではないからだ、と勝手に思ってるのだが、単に私の物覚えが悪いのだろう。

なのでメモ。