質問

VB.NET (Visual Studio 2008、.NET 3.5) でのカスタム イベントの実装に関するいくつかのヒントを探しています。

「通常の」(カスタムではない)イベントは実際にはデリゲートであることはわかっているので、カスタム イベントを実装するときにデリゲートを使用することを考えていました。一方で、 アンドリュー・トロエルセンさんの 「Pro VB 2008 と .NET 3.5 プラットフォーム」 この本ではすべてのカスタム イベントの例でコレクション タイプを使用しており、Microsoft の サンプルコード その考え方と一致します。

そこで私の質問は次のとおりです。あるデザインを他のデザインよりも選択する場合、どのような点に注意する必要がありますか?それぞれのデザインの長所と短所は何ですか?「通常の」イベントの内部実装に似ているものはどれですか?

以下は 2 つの設計を示すサンプル コードです。

Public Class SomeClass
    Private _SomeEventListeners As EventHandler
    Public Custom Event SomeEvent As EventHandler
        AddHandler(ByVal value As EventHandler)
            _SomeEventListeners = [Delegate].Combine(_SomeEventListeners, value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            _SomeEventListeners = [Delegate].Remove(_SomeEventListeners, value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            _SomeEventListeners.Invoke(sender, e)
        End RaiseEvent
    End Event

    Private _OtherEventListeners As New List(Of EventHandler)
    Public Custom Event OtherEvent As EventHandler
        AddHandler(ByVal value As EventHandler)
            _OtherEventListeners.Add(value)
        End AddHandler

        RemoveHandler(ByVal value As EventHandler)
            _OtherEventListeners.Remove(value)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            For Each handler In _OtherEventListeners
                handler(sender, e)
            Next
        End RaiseEvent
    End Event
End Class
役に立ちましたか?

解決

単純なデリゲートを使用することをお勧めします。単純に - それは すでに (内部的には) リストなので、それをラップすることで作業を複製しています。 List<T>. 。また、追加のリスト オブジェクトと配列のオーバーヘッドもあり、null になる可能性があるため、ガベージ コレクションなどへの影響が大きくなります。

また、あなたがやっている場合は、 たくさん このうち、考慮してください EventHandlerList, への効率的なアクセスを提供するために存在します。 まばらな イベント - つまり多くのイベントを公開したいが、その多くは割り当て解除できる場合。

最初の例は「標準」イベントにはるかに近いものです (ただし、呼び出し時に未割り当て/null ハンドラーを監視することもできます) Invoke, null の可能性があるため)。さらに、一部の言語 (VB が何をするのかは正直わかりません) はイベントに同期を適用しますが、実際にはイベントはほとんどないことに注意してください。 本当に スレッドセーフである必要があるため、それはやりすぎになる可能性があります。

編集 があることも発生します。 機能的な それらの違い:

  • デリゲートアプローチは、同じターゲットメソッド/インスタンスを持つ異なるインスタンスを同等のものとして扱います(私はそうではないと思います) List<T> 意思)
  • 重複したデリゲート:デリゲートは最初に最後を削除します。リストは最も早いものから順に削除します
  • 複合材料:A を追加、B を追加、(A+B) を削除 - デリゲートを使用すると null または空が返されますが、リスト手法では A と B が保持されます ((A+B) 削除では何も見つかりませんでした)

他のヒント

MulticastDelegate を使用してサブスクライブされたイベントのリストを保持することは確かに実行可能なアプローチですが、私が特に熱中しているアプローチではありません。MulticastDelegate にイベントを追加または削除するには、次の 2 つのいずれかを実行する必要があります。

  1. ロックを取得し、古いデリゲートから新しいデリゲートを作成します。ただし、イベントが追加または削除された場合は、デリゲート ポインターをそのデリゲートに設定し、ロックを解放します。
  2. 古いデリゲートへの参照をコピーし、そこから新しいデリゲートを作成します。ただし、イベントが追加または削除された場合は、古いデリゲートが変更されていない場合は Interlocked.CompareExchange を使用して新しいデリゲートへの参照を保存し、CompareExchange が失敗した場合は最初からやり直します。 。

後者のアプローチは、競合がない場合はパフォーマンスが若干向上する可能性がありますが、多くのスレッドが同時にイベントの追加または削除を試行すると、パフォーマンスが低下する可能性があります。ただし、後者のアプローチの利点の 1 つは、ロックを保持している間にスレッドが終了する危険がないことです。

どちらのアプローチも特にきれいとは思えません。デリゲートを呼び出すだけですべてのイベントを呼び出すことを計画している場合、イベント呼び出しのパフォーマンスが追加/削除のパフォーマンスを相殺する可能性があります。一方、イベント呼び出しを try/catch ブロックでラップするために GetInvocationList を使用することを計画している場合は、(適切にロックされた) リストまたはその他の同様のデータ構造を使用する方がよい場合があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top