1

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

「通常の」(非カスタム) イベントは実際にはデリゲートであることを知っているので、カスタム イベントを実装するときにデリゲートを使用することを考えていました。一方、Andrew Troelsenの本「Pro VB 2008 and the .NET 3.5 Platform」では、すべてのカスタム イベントの例でコレクション型を使用しており、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
4

2 に答える 2

3

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

また、これを頻繁に行う場合は疎なEventHandlerListイベントへの効率的なアクセスを提供するために存在する を検討してください。つまり、多くのイベントを公開したいが、それらの多くを割り当て解除できる場合です。

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

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

  • デリゲートアプローチは、同じターゲットメソッド/インスタンスを持つ異なるインスタンスを同等に扱います(私はそうは思いませんList<T>
  • デリゲートの複製: デリゲートは最後を最初に削除します。リストは最も古いものから削除します
  • コンポジット: A を追加し、B を追加し、(A+B) を削除します - デリゲートを使用すると、null / 空になるはずですが、リスト アプローチでは A と B が保持されます ((A+B) 削除では何も見つかりません)。
于 2010-04-10T10:03:25.477 に答える
0

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

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

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

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

于 2011-03-23T22:58:58.360 に答える