2009-06-01 4 views
5

私はいくつかの解決策を見つけたのはシンプルな "問題"だと思っていますが、C#のベストプラクティスに行く方法はわかりません。C#::イベントまたはインターフェイスを処理するイベントから派生したオブジェクトのコレクションを使用する場合

私は、マスターオブジェクト(シングルトンなど)がアプリケーションの有効期間中に一度インスタンス化されています。この「MasterClass」は、MasterClass.Instance.CreateSlaveObjectが呼び出されるたびに「SlaveClass」という新しい種類のオブジェクトを作成します。

このMasterClassは、他のオブジェクトのステータス変更も監視し、変更が発生した場合は、変更の作成したSlaveClassオブジェクトに通知します。シンプルだと思われる。

私はネイティブC++の世界から来たので、私は「SlaveClassを」派生

Interface IChangeEventListener 
{ 
    void ChangeHappened(); 
} 

そこからインターフェイスを持つように最初にそれをやった方法。それから私の "MasterClass"で私は持っています:

... 
IList<IChangeEventListener> slaveList; 
... 
CreateSlaveObject 
{ 
    ... 
    slaveList.Add(slave); 
} 
... 
ChangeHappened() 
{ 
    ... 
    foreach(var slave in slaveList) 
    { 
     slave.ChangeHappened(); 
    } 
} 

これは動作します。しかし、これをやるための別の(より良い)方法があれば、私は心の後ろで不思議に思っていました。だから私はトピックについてもう少し研究し、C#イベントを見た。

したがって、MasterClassにスレーブのコレクションを保持する代わりに、基本的にMasterClassをSlaveClassのctor(またはプロパティを介して)に挿入し、SlaveClassオブジェクトにChangeHappenedをイベントハンドラとして追加させます。

...Master...   
    public delegate void ChangeHappenedDelegate(object sender, NewsInfoArgs args); 
    public event NewUpdateDelegate ChangeHappenedEvent; 
    .... 

    public SlaveClass (MasterClass publisher) //inject publisher service 
    { 
     publisher.ChangeHappenedEvent += ChangeHappened; 
    } 

しかし、これはスレーブとマスターとの間に不必要なカップリングのようなもののようですが、私は提供ビルドでのイベント通知メカニズムの優雅さを好む:これが示されることになります。

私は現在のコードを保持するか、イベントベースのアプローチ(パブリッシャー注入)に移行する必要がありますか?なぜ?

また、私が逃したかもしれない代替ソリューションを提案できれば、私もそれに感謝します。

答えて

7

まあ、私の考えでは、あなたのようなイベントとインターフェイスは、同じコインの2つの側面(少なくともあなたがそれを記述した文脈では)ですが、実際には2つですです。

イベントについて私が考えることは、「あなたに何か起きたときに私に教えてほしいから、あなたのイベントに登録する必要がある」ということです。

インターフェイス方法は「私に何か起こったことを知らせるためにメソッドを呼び出す必要があります」です。

同じように聞こえるかもしれませんが、誰が話しているかは異なります。どちらの場合も、話しているのは「マスタークラス」であり、それによってすべての違いが生じます。

マスタークラスで何か起きたときに呼び出すのに適したメソッドがスレーブクラスにある場合、スレーブクラスにこれをフックするコードを含める必要はないことに注意してくださいあなたのCreateSlaveClassの方法でこれを実行します。

SlaveClass sc = new SlaveClass(); 
ChangeHappenedEvent += sc.ChangeHappened; 
return sc; 

これは、基本的にはイベントシステムを使用しますが、マスタークラスのコードは、イベントのすべての配線を行うことができます。

SlaveClassオブジェクトはシングルトンクラスの長さで存続しますか?そうでない場合は、上記のケース(基本的にあなたと私の両方)のように、古くなった/不要になったケースを処理する必要があります。マスタークラスのオブジェクトへの参照を保持しています。これらのイベントを強制的に削除したり、インターフェイスの登録を解除したりしない限り、ガベージコレクションの対象にはなりません。


SlaveClassは限りマスタークラスとして生きていないとの問題を処理するために、あなたもコメントで述べたように、あなたは、同じカップリングの問題に実行するつもりです。

SlaveClassオブジェクトの正しいメソッドに実際にリンクするのではなく、内部的にこのメソッドを呼び出すラッパーオブジェクトを作成することができます(引用符に注意してください)。この利点は、ラッパーオブジェクトがWeakReferenceオブジェクトを内部的に使用できることです。そのため、SlaveClassオブジェクトがガベージコレクションに適格になると、そのオブジェクトが収集され、次回に正しいメソッドを呼び出そうとすると、これに気づくでしょうし、あなたはきれいにしなければなりません。このような例えば

、(私は、Visual Studioのインテリセンスとコンパイラの恩恵なしで入力することだし、ここでは、構文(エラー)このコードの意味を取り、ないてください。)

public class WrapperClass 
{ 
    private WeakReference _Slave; 

    public WrapperClass(SlaveClass slave) 
    { 
     _Slave = new WeakReference(slave); 
    } 

    public WrapperClass.ChangeHappened() 
    { 
     Object o = _Slave.Target; 
     if (o != null) 
      ((SlaveClass)o).ChangeHappened(); 
     else 
      MasterClass.ChangeHappenedEvent -= ChangeHappened; 
    } 
} 
SlaveClassオブジェクトが収集されるとのそれらを知らせるために、イベントハンドラへのあなたのマスタークラスから次の呼び出しを(ただし早くより)

SlaveClass sc = new SlaveClass(); 
WrapperClass wc = new WrapperClass(sc); 
ChangeHappenedEvent += wc.ChangeHappened; 
return sc; 

:あなたのマスタークラスで

は、あなたは、このように、このような何かをするだろう変更すると、もはやオブジェクトを持たないラッパー 除去される。

+0

ニート!私はあなたがMasterClassに提案したアプローチが本当に好きですが、イベントの仕組みを維持しています:)私が見る問題はあなたが尋ねたように、スレーブがマスターと同じくらい長く生きるという保証はありません...だから私はスレーブデストラクタでイベントをアンフックしたい場合、私はカップリングを戻すでしょう:/ – Futurist

+0

まあ、代替があります、私の答えを変更させてください。 –

+0

返信ありがとうございました。私はまだ私に慣れていないいくつかの概念(弱参照のような)があるので、これを熟考します。 – Futurist

0

私はそれが個人的な好みの問題だと思っています...個人的には、.NETの "哲学"がうまく適合しているので、私はイベントを使うのが好きです。あなたのケースでは

マスタークラスはシングルトンである場合、あなたはそれがシングルトンプロパティ(またはメソッド)を使用して取得することができるため、SlaveClassのコンストラクタにそれを渡す必要はありません。

public SlaveClass() 
{ 
    MasterClass.Instance.ChangeHappenedEvent += ChangeHappened; 
} 
+0

私はちょっと嘘をついていました。それは本当にシングルトンではありません:)しかし、スレーブはマスターがシングルトンだったとしてもマスターを認識していなければならないと思っています。それにもかかわらず、返信をありがとう。 – Futurist

0

MasterClassのインスタンスが1つしかないようですが、MasterClass.Instance.ChangeHappenedEventを購読してみませんか?それはまだタイトなアイカップリングですが、比較的きちんとしています。

+0

私は少しだけ嘘をつきました。それは本当にシングルトンではありません:)しかし、スレーブはマスターがシングルトンだったとしてもマスターを知っていなければならないと思っています。それにもかかわらず、返信をありがとう。 – Futurist

0

イベントはパブリックサブスクライブ/サブスクライブ機能を公開するための通常のパラダイムですが、多くの場合サブスクリプション/サブスクライブ解除機能を公開する必要がない場合には最良のパラダイムではありません。このシナリオでは、イベントの購読/購読を解除できるのは自分で作成したものだけなので、公開購読/購読解除の方法は必要ありません。さらに、奴隷に関するマスターの知識は、一般的なイベントパブリッシャーの加入者知識をはるかに超えています。したがって、私はマスターが明示的にサブスクリプションの接続/切断を処理することを望んでいます。

しかし、マスターとスレーブのカップリングによってやや簡単になっている機能は、外部参照がすべて破棄された場合にスレーブがガベージコレクションされるようにする手段になります。これを行う最も簡単な方法は、おそらくマスターが 'WeakReference'の奴隷のリストを保持させることでしょう。奴隷に何かが起こったことを通知することが必要なときは、リストを参照して、まだ生きているWeakReferenceを参照解除し、スレーブに通知します。最後のスイープ以降にリストのアイテムの半分以上が追加されていて、リストに250以上のアイテムが含まれている場合は、すべてのライブ参照を新しいリストにコピーし、古いリストを置き換えます。

WeakReferenceオブジェクトの参照を頻繁に逆参照することを避ける別のアプローチは、パブリック "スレーブ"オブジェクトをプライベートオブジェクトへのラッパーにし、パブリックオブジェクトをFinalizeでオーバーライドして、プライベートオブジェクトにそれに対する外部参照は存在しません。これには、オブジェクトへのパブリックアクセスのための強力な間接指示のレベルを追加する必要がありますが、放棄されたオブジェクトへの瞬間的な参照を作成することは避けられます。

関連する問題