ラベル デリゲート の投稿を表示しています。 すべての投稿を表示
ラベル デリゲート の投稿を表示しています。 すべての投稿を表示

2009年9月26日土曜日

非同期デリゲート

.net では Thread、タイマーなどマルチスレッドを実現する方法が
いくつか用意されています。
今回のデリゲートもそのうちのひとつで、
スレッドの優先順位など細かい制御はできないものの、
任意の型のパラメーターをいくつでも渡せたり、
戻り値が簡単に取れるのはなにかと使い勝手がよいと思います。

実際の使用例は以下の通りです。

// 何かするデリゲート
delegate string SomeDelegate();
private void button1_Click(object sender, EventArgs e)
{
    // デリゲートの実装
    SomeDelegate some = new SomeDelegate(delegate
    {
        // 5 秒後 "hoge" を返します。
        System.Threading.Thread.Sleep(5000);
        return "hoge";
    });

    // SomeDelegate を非同期に呼び出します。
    IAsyncResult ar = some.BeginInvoke(null, null);

    // SomeDelegate から戻り値を得ます。
    Console.WriteLine("returned : " + some.EndInvoke(ar));
}


ボタンを押して 5 秒後、出力ウィンドウに

returned : hoge

と出力されます。

ただし、上の例では EndInvoke() をメインスレッドで
呼び出すことになるので、デリゲートが実行されている間
せっかく非同期に呼び出したのにメインスレッドが停止してしまいます。
(今回の場合、フォームが固まります。)
できればこれは避けたいので、少し実装に手間が増えますが
コールバック関数を用意します。

{
    /* BeginInvoke() の直前まで同じなので省略 */

    // AsyncCallback デリゲート
    AsyncCallback callback = new AsyncCallback(some_Completed);

    // 第一引数にコールバックメソッドを指定します。
    IAsyncResult ar = some.BeginInvoke(callback, null);
}

// SomeDelgate の終了時にコールされます。
private void some_Completed(IAsyncResult async)
{
    // AsyncResult にキャストします。
    AsyncResult asyncResult = (AsyncResult)async;

    // AsyncDelegate で SomeDelegate のインスタンスが取得できます。
    SomeDelegate some = (SomeDelegate)asyncResult.AsyncDelegate;
    Console.WriteLine("returned : " + some.EndInvoke(async));
}


メインスレッドでデリゲートの完了を待たないので
処理自体は先ほどと同じですが、フォームが固まることはありません。

最後になりましたが、デリゲート自体は ThreadPool を利用した技術なので
同時実行数には制限があります。
場合によってはデッドロックが発生することもあるそうなのですが、
特に必要に迫られたことがないのでこの辺あんまり調べていないです。
まぁそんな依存のある非同期処理ならコールバックで
次の非同期処理を BeginInvoke すればよいかと思います。

2009年9月9日水曜日

イベントの実装

イベントを独自に実装するにはデリゲートを使います。
注意点は、イベントを発行する前に if (null != MyEvent) { ... } のように
呼び出し元でイベントハンドラーが追加されていることを
必ず確認することです。

public class EventClass
{
    public void DoSomething()
    {
        // イベントハンドラーの登録を確認します。
        if (null != MyEvent)
        {
            MyEvent(this, new MyEventArgs("Occurred MyEvent"));
        }
    }

    // イベントデリゲート
    public delegate void MyEventHandler(object sender, MyEventArgs e);
    // イベント
    public event MyEventHandler MyEvent;
}

// EventArgs を継承して独自のイベントデータを定義します。
public class MyEventArgs : EventArgs
{
    public MyEventArgs(string myVar)
    {
        this.myVar = myVar;
    }

    public string MyVar
    {
        get { return myVar; }
        set { myVar = value; }
    }

    private string myVar;
}

// イベントを受け取る側の実装
EventClass eventClass = new EventClass();
// イベントハンドラーを追加します。
eventClass.MyEvent += delegate(object sender, MyEventArgs e)
{
    MessageBox.Show(e.MyVar);
};

eventClass.DoSomething();








eventClass.MyEvent に追加したデリゲートが実行され、
メッセージボックスが表示されます。

ただ、今回のようにイベントの引数が object と EventArgs の派生クラスの
2 つで構成されるときは
System.EventHandler<TEventargs> デリゲートを使用し、
次のように書くこともできます。(1 ステップ減るだけですが)

public class EventClass
{
    public void DoSomething()
    {
        if (null != MyEvent)
        {
            MyEvent(this, new MyEventArgs("Occurred MyEvent"));
        }
    }

    public event EventHandler<MyEventArgs> MyEvent;
}