使い捨てのUIコントロールであるクラスがあります。モルデルオブジェクトの変更をサブスクライブして、コンテンツを再描画します。
一方、状況によっては、同じモデルオブジェクトの特別な変更により、このコントロールを含むビューに、このコントロール(コントロール)を削除して破棄するように指示されます。
その結果、サブスクリプションの順序に応じてモデルが変更されます(最初にコントロールが破棄され、その後メソッドが呼び出されます) 。これは最終的には。になりObjectDisposedException
ます。
質問:コントロールはイベントコールバックを安全に無視するように設計する必要がありますか、それとも他のレイヤーからのこの種の呼び出しを防ぐようにする必要がありますか?
単語よりも多くのコードを見たい人のために、私は非常に単純化された例を用意しました:
//############################################
class View
{
private Control m_Control;
public View(Logic logic, Model model)
{
m_Control = new Control(model);
logic.Changed += LogicChanged;
}
private void LogicChanged(object sender, EventArgs e)
{
m_Control.Dispose();
m_Control = null;
}
}
//############################################
class Control : IDisposable
{
private readonly Model m_Model;
public Control(Model model)
{
m_Model = model;
m_Model.Changed += ModelOnChanged;
}
public bool IsDisposed { get; private set; }
public void Dispose()
{
m_Model.Changed -= ModelOnChanged;
IsDisposed = true;
}
private void ModelOnChanged(object sender, EventArgs e)
{
if (IsDisposed)
{
throw new ObjectDisposedException(ToString());
}
//Do something
}
}
//############################################
class Model
{
public event EventHandler<EventArgs> Changed;
private void OnChanged(EventArgs e)
{
EventHandler<EventArgs> handler = Changed;
if (handler != null)
handler(this, e);
}
public void Change()
{
OnChanged(null);
}
}
//############################################
class Logic
{
private readonly Model m_Model;
public Logic(Model model)
{
m_Model = model;
m_Model.Changed += ModelOnChanged;
}
private void ModelOnChanged(object sender, EventArgs e)
{
OnChanged(null);
}
public event EventHandler<EventArgs> Changed;
private void OnChanged(EventArgs e)
{
EventHandler<EventArgs> handler = Changed;
if (handler != null)
handler(this, e);
}
}
//############################################
class Program
{
private static void Main(string[] args)
{
var model = new Model();
var logic = new Logic(model);
var view = new View(logic, model);
model.Change();
//And crash!
}
}
与えられた例のどこで修正を提案しますか?Model
そして、Logic
クラスはイベントのサブスクリプションの順序を知らずにビジネスを行っているだけです。また、設計上の欠陥View
やControl
実装も見当たりません。
Model
を実装している3つの異なるチームがあり、これら4つのコンポーネントだけでなく、何百ものコンポーネントがあると想像してくださいLogic
。UI
この問題はどこでも発生する可能性があります。
私が探しているのは、この特定のケースでのローカル修正ではありませんが、それを防ぐためのパターンを見つけたいと思います。例:「コントロールは、破棄されたインスタンスでのイベント呼び出しを適切に無視する必要があります」または「ロジックはモデルでのサブスクリプションを防止する必要があります。UIのみがこれを許可されます。」等
容認された答えに加えて
はい、破棄されたオブジェクトのイベントコールバックは例外をスローするべきではありません。さらに一般的に:
...イベントハンドラーは、イベントのサブスクライブが解除された後でも呼び出される場合でも、堅牢である必要があります。
それにはいくつかの理由があります-EricLippertの素晴らしい記事EventsandRacesを参照してください