69

私は次のものを持っています(簡略化):

interface IFindFilesObserver
{
    void OnFoundFile(FileInfo fileInfo);
    void OnFoundDirectory(DirectoryInfo directoryInfo);
}

class FindFiles
{
    IFindFilesObserver _observer;

    // ...
}

...そして、私は矛盾しています。これは基本的に C++ で記述したものですが、C# にはイベントがあります。イベントを使用するようにコードを変更する必要がありますか、それともそのままにしておく必要がありますか?

従来のオブザーバー インターフェイスに対するイベントの長所と短所は何ですか?

4

11 に答える 11

77

イベントは、インターフェイスにメソッドが 1 つしかないコールバック インターフェイスであると考えてください。

必要なイベントのみをフックする イベント
では、処理したいイベントのハンドラーを実装するだけで済みます。オブザーバー インターフェース パターンでは、実際には処理を気にしない通知タイプのメソッド本体の実装を含め、インターフェース全体にすべてのメソッドを実装する必要があります。あなたの例では、これらのイベントの 1 つだけを気にする場合でも、常に OnFoundDirectory と OnFoundFile を実装する必要があります。

メンテナンス
の軽減 イベントのもう 1 つの利点は、特定のクラスに新しいイベントを追加して、それを発生させることができることです。既存のすべてのオブザーバーを変更する必要はありません。一方、インターフェイスに新しいメソッドを追加する場合は、そのインターフェイスを既に実装しているすべてのクラスを調べて、それらすべてに新しいメソッドを実装する必要があります。ただし、イベントの場合は、追加する新しいイベントに応じて実際に何かを実行したい既存のクラスを変更するだけで済みます。

パターンは言語に組み込まれているので、誰もがその使い方を
知っています。オブザーバー インターフェイスでは、通知を受信し、オブザーバーを接続するためのさまざまな登録方法を実装することがよくあります。ただし、イベントを登録して使用する方法 (+= 演算子を使用) を学べば、残りはすべて同じ。

インターフェースの長所 インターフェース
の長所 はあまりありません。私は彼らが誰かにインターフェースのすべてのメソッドを実装するように強制していると思います. しかし、誰かにこれらすべてのメソッドを正しく実装するよう強制することはできないので、これにはあまり価値がないと思います。

構文
イベントごとにデリゲート型を宣言しなければならない方法を好まない人もいます。また、.NET フレームワークの標準イベント ハンドラーには、次のパラメーターがあります: (オブジェクト送信者、EventArgs args)。送信者は特定のタイプを指定しないため、使用する場合はダウンキャストする必要があります。これは多くの場合、実際には問題ありませんが、静的型システムの保護が失われているため、あまり正しくないように感じます。ただし、独自のイベントを実装し、これに関する .NET フレームワークの規則に従わない場合は、適切な型を使用できるため、潜在的なダウンキャストは必要ありません。

于 2009-02-15T12:35:16.227 に答える
31

うーん、イベントを使用して Observer パターンを実装できます。実際、イベントを使用することは、観察者パターンの別の実装と見なすことができます。

于 2009-02-15T12:14:45.057 に答える
11
  • FACADE パターンを使用したり、作業を他のクラスに委譲したりする場合など、オブジェクトのチェーンを介してイベントを伝播するのは困難です。
  • オブジェクトがガベージ コレクトされるようにするには、イベントのサブスクライブを解除する際に細心の注意を払う必要があります。
  • イベントは、単純な関数呼び出しよりも 2 倍遅く、発生ごとに null チェックを行うと 3 倍遅くなり、null チェックと呼び出しの前にイベント デリゲートをコピーしてスレッド セーフにします。

  • 新しい (4.0 の)インターフェイスについては、MSDNもお読みください。IObserver<T>

次の例を検討してください。

using System;

namespace Example
{
    //Observer
    public class SomeFacade
    {
        public void DoSomeWork(IObserver notificationObject)
        {
            Worker worker = new Worker(notificationObject);
            worker.DoWork();
        }
    }
    public class Worker
    {
        private readonly IObserver _notificationObject;
        public Worker(IObserver notificationObject)
        {
            _notificationObject = notificationObject;
        }
        public void DoWork()
        {
            //...
            _notificationObject.Progress(100);
            _notificationObject.Done();
        }
    }
    public interface IObserver
    {
        void Done();
        void Progress(int amount);
    }

    //Events
    public class SomeFacadeWithEvents
    {
        public event Action Done;
        public event Action<int> Progress;

        private void RaiseDone()
        {
            if (Done != null) Done();
        }
        private void RaiseProgress(int amount)
        {
            if (Progress != null) Progress(amount);
        }

        public void DoSomeWork()
        {
            WorkerWithEvents worker = new WorkerWithEvents();
            worker.Done += RaiseDone;
            worker.Progress += RaiseProgress;
            worker.DoWork();
            //Also we neede to unsubscribe...
            worker.Done -= RaiseDone;
            worker.Progress -= RaiseProgress;
        }
    }
    public class WorkerWithEvents
    {
        public event Action Done;
        public event Action<int> Progress;

        public void DoWork()
        {
            //...
            Progress(100);
            Done();
        }
    }
}
于 2012-03-27T08:12:20.940 に答える
8

インターフェイス ソリューションの利点:

  • メソッドを追加する場合、既存のオブザーバーはそれらのメソッドを実装する必要があります。これは、既存のオブザーバーを新しい機能に結び付けるのを忘れる可能性が少なくなることを意味します。もちろん、それらを空のメソッドとして実装することもできます。つまり、特定の「イベント」に応答して何もしないという贅沢があります。でもそう簡単には忘れられません。
  • 明示的な実装を使用すると、別の方法でコンパイラ エラーが発生します。既存のインターフェイスを削除または変更すると、それらを実装するオブザーバーがコンパイルを停止します。

短所:

  • オブザーバー インターフェイスを変更すると、ソリューション全体に変更が加えられる可能性があり、別の計画が必要になる可能性があるため、計画にはさらに検討が必要です。単純なイベントはオプションであるため、他のコードがイベントに反応する必要がない限り、他のコードを変更する必要はほとんどありません。
于 2009-02-15T12:40:48.163 に答える
7

イベントのその他の利点。

  • 無料で適切なマルチキャスト動作を取得できます。
  • そのイベントに応答してイベントのサブスクライバーを変更すると、動作が明確に定義されます
  • 簡単かつ一貫して内省 (反映) できる
  • イベントに対するツール チェーンのサポート (単純に、イベントが .net のイディオムであるため)
  • 提供する非同期 API を使用するオプションが得られます

これらすべて (ツール チェーンを除く) を自分で実現することはできますが、驚くほど困難です。例: List<> のようなメンバー変数を使用して、オブザーバーのリストを格納する場合。foreach を使用してそれを反復処理する場合、OnFoo() メソッド コールバックのいずれかでサブスクライバーを追加または削除しようとすると、それをきれいに処理するコードをさらに記述しない限り、例外がトリガーされます。

于 2009-02-15T19:14:47.607 に答える
3

長所は、イベントがより「ドットネット」であることです。フォームにドロップできる非ビジュアル コンポーネントを設計している場合は、デザイナーを使用してそれらを接続できます。

短所は、イベントが単一のイベントのみを意味することです。オブザーバーに通知したい「もの」ごとに個別のイベントが必要です。これは、観測される各オブジェクトがすべてのイベントのすべてのオブザーバーの参照を保持する必要があり、観測されるオブジェクトが多数ある場合にメモリを肥大化させることを除いて、実際にはあまり実用的な影響はありません (別の方法で作成した理由の 1 つです)。 WPF でのオブザーバー/オブザーバブルの関係の管理)。

あなたの場合、大した違いはないと私は主張します。通常、オブザーバーがこれらすべてのイベントに関心がある場合は、個別のイベントではなく、オブザーバー インターフェイスを使用します。

于 2009-02-15T12:18:35.220 に答える
3

決定する最良の方法は次のとおりです。どちらが状況に適しているかです。ばかげた、または役に立たない答えのように聞こえるかもしれませんが、どちらか一方を「適切な」解決策と見なすべきではないと思います。

私たちはあなたに百のヒントを投げることができます. イベントは、オブザーバーが任意のイベントをリッスンすることが期待される場合に最適です。オブザーバーが特定の一連のイベントのすべてにリストされることが期待される場合、インターフェースが最適です。イベントは、GUI アプリを扱う場合に最適です。インターフェイスが消費するメモリが少なくなります (複数のイベントに対する単一のポインター)。ヤダヤダヤダ。長所と短所の箇条書きリストは考慮すべきものですが、決定的な答えではありません. 本当に必要なのは、実際のアプリケーションで両方を試して、良い感触を得ることです。次に、状況に適したものを選択できます。フォームを学ぶ。

1 つの明確な質問を使用する必要がある場合は、どちらが状況をよりよく表しているかを自問してください: 使用または無視される可能性のある疎関連イベントのセット、または一般的にすべてが処理する必要がある密接に関連するイベントのセット一人のオブザーバー。しかし、ここではイベント モデルとインターフェイス モデルについて説明しているだけなので、振り出しに戻ります。状況に適しているのはどちらでしょうか。

于 2009-02-15T17:05:31.380 に答える
2

私は次の理由でイベントベースのソリューションを好みます

  • エントリーコストを削減します。本格的なインターフェースを実装するよりも、「+=newEventHandler」と言う方がはるかに簡単です。
  • メンテナンスコストを削減します。クラスに新しいイベントを追加する場合は、それだけで十分です。インターフェイスに新しいイベントを追加する場合は、コードベース内のすべてのコンシューマーを更新する必要があります。または、時間の経過とともに消費者に迷惑をかけるまったく新しいインターフェイスを定義します。「IRandomEvent2またはIRandomEvent5を実装しますか?」
  • イベントを使用すると、ハンドラーを非クラスベース(つまり、どこかに静的メソッド)にすることができます。すべてのイベントハンドラーを強制的にインスタンスメンバーにする機能的な理由はありません
  • 一連のイベントをインターフェースにグループ化することは、イベントがどのように使用されるかについての仮定を立てています(そしてそれはまさにそれであり、仮定です)
  • インターフェースは、生のイベントに勝る本当の利点を提供しません。
于 2009-02-15T14:32:50.840 に答える
2

Javaは匿名インターフェースの言語サポートを備えているため、コールバックインターフェースはJavaで使用するものです。

C#は匿名デリゲート(ラムダ)をサポートしているため、イベントはC#で使用するものです。

于 2009-02-15T14:36:27.387 に答える
2

インターフェイスの利点は、デコレータを簡単に適用できることです。標準的な例:

subject.RegisterObserver(new LoggingObserver(myRealObserver));

に比べ:

subject.AnEvent += (sender, args) => { LogTheEvent(); realEventHandler(sender, args); };

(私はデコレータ パターンの大ファンです)。

于 2010-10-27T11:49:11.920 に答える
1

NetDataContractSerializerなどの参照を保持する何らかの方法でオブジェクトをシリアル化する必要がある場合、またはおそらくprotobufイベントはシリアル化の境界を越えることができません。オブザーバー パターンはオブジェクト参照だけに依存するため、必要に応じてこのタイプのシリアル化を問題なく使用できます。

元。Web サービスに渡す必要がある、相互に双方向にリンクするビジネス オブジェクトが多数あります。

于 2011-03-17T17:09:21.883 に答える