5

匿名イベント ハンドラーについて、かなり短い質問があります。

これは私が持っているコードです:

public void AddTestControl(Control ctrl)
{
    ctrl.Disposed += (o, e) => { RemoveTestControl(ctrl); };
    ctrl.SomeEvent += _Control_SomeEvent;
}

public void RemoveTestControl(Control ctrl)
{
    ctrl.SomeEvent -= _Control_SomeEvent;
}

上記のコードは問題ありませんか、それとも Disposed Event Handler を削除するためにコードを書き直す必要がありますか? このようなもの:

public void AddTestControl(Control ctrl)
{
    ctrl.Disposed += _Control_Disposed;
    ctrl.SomeEvent += _Control_SomeEvent;
}

public void RemoveTestControl(Control ctrl)
{
    ctrl.Disposed -= _Control_Disposed;
    ctrl.SomeEvent -= _Control_SomeEvent;
}
4

3 に答える 3

8

一般に、ガベージ コレクションの対象にするためにオブジェクトからイベント ハンドラーを削除する必要がある唯一の状況は、パブリッシャーオブジェクト (イベントを定義するオブジェクト) がサブスクライバーオブジェクト (イベントを含むオブジェクト) よりも長く存続する場合です。ハンドラー)。このような状況では、サブスクライバーがまだパブリッシャーによって参照されているため、スコープ外になったときに GC はサブスクライバーを解放できません。

この場合、これが WebForms または WinForms であると仮定すると、パブリッシャー (つまりControlオブジェクト) はサブスクライバー (おそらく aまたは a ) の子オブジェクトである可能性が高く、関連するすべてのものを取得して最初にスコープ外になります。それを持つオブジェクト。したがって、イベント ハンドラーを削除する必要はありませんPageForm

于 2012-03-02T10:11:43.267 に答える
2

サブスクライバーが常にパブリッシャー(イベントを発生させるオブジェクト)よりも長生きすることがわかっている状況でも、イベントのサブスクライブを解除する方が常にクリーンに感じられます。イベントが再び発生することはなく、パブリッシャーには到達できず、収集できます。

繰り返しになりますが、WinForms アプリケーションなどですべてのイベント ハンドラーをサブスクライブ解除するのに何人の人が苦労するでしょうか? オブジェクト参照はパブリッシャーからサブスクライバーを指すので、サブスクライバーが存続している間にパブリッシャーを収集できます。反対の状況と同じ危険性はありません。たとえば、存続期間の長いパブリッシャー (静的イベントなど) が、潜在的に大規模なサブスクライバーを、収集された後も無駄に存続させる可能性があります。

サブスクライブを解除する必要がある場合は、同じデリゲートのサブスクライブを解除する必要があるため、匿名のイベント ハンドラーが少し面倒になります。Reactive Extensionsは、この問題を巧妙な方法で解決します。サブスクライブしたデリゲートを覚えておく必要はなく、サブスクライブすると、IDisposable破棄されるとサブスクライブを解除する が返されます。すべてのサブスクリプションを にまとめることで、電話CompositeDisposable1 回ですべてのサブスクリプションを解除できますDispose

于 2012-03-02T10:38:27.810 に答える
1

どちらの codez も問題ありませんが、個人的な好みの問題として 2 番目が好きです。最初よりはっきり読めます。

その上に最初のコードがあり、匿名のラムダ デリゲートがあり、ctrl への現在の参照を取得します。そのコードは、状況とコンパイルの最適化設定 (呼び出しがインライン化されているかどうかにかかわらず) によって予期しない動作をする可能性があります。

コードに構造上の問題があることは言うまでもありません。ControlOwner と多数の子コントロールがあります。実行時に子コントロールを ControlOwner に追加し、ControlOwner を childControl イベントにサブスクライブすることで、その動作に対応しようとしていると思います。これは、_ButtonClicked などのイベントには問題ありませんが、Dispose には適していません。子コントロールがそれ自体で処理するようにします。OwnerControl はそれについて知る必要はありません。ChildControl[n].Dispose が呼び出された時点で存在しない可能性があることは言うまでもありません。

要するに: * Dispose イベントを ChildControl だけに残し、ChildControl.Dispose ですべてのクリーンアップを行うことをお勧めします * イベントのサブスクライブを解除する必要はありません。イベント ディスパッチャーは、サブスクライバーが生きているかどうかを確認します。

于 2012-03-02T10:45:52.377 に答える