私は次のものを持っています(簡略化):
interface IFindFilesObserver
{
void OnFoundFile(FileInfo fileInfo);
void OnFoundDirectory(DirectoryInfo directoryInfo);
}
class FindFiles
{
IFindFilesObserver _observer;
// ...
}
...そして私は対立しています。これは基本的にはC++で記述したものですが、C#にはイベントがあります。イベントを使用するようにコードを変更する必要がありますか、それともそのままにする必要がありますか?
従来のオブザーバーインターフェイスを超えるイベントの利点または欠点は何ですか?
イベントがコールバックインターフェースであり、インターフェースにメソッドが1つしかない場合を考えます。
必要なフックイベントのみ
イベントでは、処理したいイベントのハンドラーを実装するだけで済みます。オブザーバーインターフェイスパターンでは、実際に処理する必要のない通知タイプのメソッド本体の実装を含め、インターフェイス全体ですべてのメソッドを実装する必要があります。あなたの例では、これらのイベントの1つだけに関心がある場合でも、常にOnFoundDirectoryとOnFoundFileを実装する必要があります。
メンテナンスが少ない
イベントのもう1つの良い点は、特定のクラスに新しいイベントを追加して、それを発生させることができ、既存のオブザーバーをすべて変更する必要がないことです。一方、新しいメソッドをインターフェイスに追加する場合は、そのインターフェイスを既に実装しているすべてのクラスを回避し、それらすべてに新しいメソッドを実装する必要があります。ただしイベントの場合、追加する新しいイベントに応じて実際に何かを実行したい既存のクラスを変更するだけで済みます。
パターンは言語に組み込まれているので、誰もがそれを使用する方法を知っています
イベントは慣用的であり、イベントを見ると、その使用方法がわかります。オブザーバーインターフェイスを使用すると、通知を受信してオブザーバーをフックするためのさまざまな登録方法を実装することがよくあります。ただし、イベントを登録して使用する方法(+ =演算子を使用)を習得したら、残りはすべてです。同じ。
インターフェースの長所
インターフェイスのプロはあまりいません。彼らは誰かにインターフェースのすべてのメソッドを実装するように強制していると思います。しかし、実際に誰かにこれらすべてのメソッドを正しく実装するよう強制することはできないので、これには多くの価値があるとは思いません。
構文
一部の人々は、各イベントのデリゲートタイプを宣言する必要がある方法を好みません。また、以下の.Netフレームワークの標準イベントハンドラーには、これらのパラメーターがあります(オブジェクト送信者、EventArgs引数)。送信者は特定のタイプを指定しないため、使用する場合はダウンキャストする必要があります。静的型システムの保護を失っているので、これは実際には大抵問題ありません。ただし、独自のイベントを実装し、これに関する.Netフレームワークの規則に従っていない場合は、正しいタイプを使用できるため、ダウンキャストの可能性はありません。
うーん、イベントを使用してObserverパターンを実装できます。実際、イベントの使用は、オブザーバーパターンimhoの別の実装と見なすことができます。
インターフェイスソリューションの長所:
短所:
イベントのいくつかのさらなる利点。
これらすべて(ツールチェーンを除く)を自分で実現できますが、それは驚くほど困難です。例:List <>のようなメンバー変数を使用してオブザーバーのリストを格納する場合。 foreachを使用して反復する場合、OnFoo()メソッドコールバックの1つ内でサブスクライバーを追加または削除しようとすると、それを適切に処理するためのコードを記述しない限り、例外がトリガーされます。
イベントは、単純な関数呼び出しよりも2倍遅く、すべてのレイズでnullチェックを実行すると3倍遅くなり、nullチェックと呼び出しの前にイベントデリゲートをコピーしてスレッドセーフにします。
この例を考えてみましょう:
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();
}
}
}
長所は、イベントはより「ドットネット」であるということです。フォームにドロップできる非ビジュアルコンポーネントを設計している場合は、デザイナーを使用してそれらを接続できます。
短所は、イベントが単一のイベントを示すだけであることです。オブザーバーに通知する「もの」ごとに個別のイベントが必要です。これは実際にはそれほど実際的な影響はありませんが、観測された各オブジェクトは、すべての観測者の参照をすべてのイベントに対して保持する必要があり、観測されたオブジェクトが多数ある場合にメモリを膨らませる必要があります(異なる方法で作成した理由の1つ) WPFでのオブザーバー/オブザーバブル関係の管理)。
あなたの場合、それは大きな違いはないと主張します。通常、オブザーバーがこれらすべてのイベントに関心がある場合は、個別のイベントではなく、オブザーバーインターフェイスを使用します。
Javaは匿名インターフェースの言語サポートを備えているため、コールバックインターフェースはJavaで使用するものです。
C#は匿名のデリゲート(ラムダ)をサポートしているため、イベントはC#で使用するものです。
決定する最良の方法はこれです。どちらが状況により適していますか。それはばかげているか役に立たない答えのように聞こえるかもしれませんが、私はあなたがどちらか一方を「適切な」ソリューションと見なすべきではないと思います。
私たちはあなたに100のヒントを投げることができます。イベントは、オブザーバーが任意のイベントをリッスンすることが期待される場合に最適です。インターフェイスは、オブザーバーが特定のイベントセットすべてにリストされることが期待される場合に最適です。イベントは、GUIアプリを扱うときに最適です。インターフェースは、より少ないメモリを消費します(複数のイベントに対する単一のポインター)。ヤッダヤッダヤッダ賛否両論の箇条書きのリストは、検討すべきことですが、決定的な答えではありません。実際に必要なのは、実際のアプリケーションで両方を試してみて、それらの感触を良くすることです。次に、状況により適したものを選択できます。フォームを学ぶ。
単一の定義に関する質問を使用する必要がある場合は、状況をよりよく説明しているものを自問してください。使用または無視される可能性のある緩やかに関連するイベントのセット、またはすべてが一般に処理する必要がある密接に関連するイベントのセット一人の観察者。しかし、それでは、イベントモデルとインターフェイスモデルについて説明しているだけなので、1に戻ります。どちらが状況により適していますか?
インターフェースの利点は、デコレーターをより簡単に適用できることです。標準的な例:
subject.RegisterObserver(new LoggingObserver(myRealObserver));
に比べ:
subject.AnEvent += (sender, args) => { LogTheEvent(); realEventHandler(sender, args); };
(私はデコレータパターンの大ファンです)。
次の理由で、イベントベースのソリューションを好みます
NetDataContractSerializer または protobuf などの参照を保持する方法でオブジェクトをシリアル化する必要がある場合、イベントはシリアル化境界を越えることができません。オブザーバーパターンはオブジェクト参照だけに依存しているため、このタイプのシリアル化で問題がなければ問題なく機能します。
例Webサービスに渡す必要がある、双方向に相互にリンクする多数のビジネスオブジェクトがあります。