Пользовательские события - Соображения реализации списка привозок

StackOverflow https://stackoverflow.com/questions/2608959

Вопрос

Я ищу некоторых указателей на реализацию пользовательских событий в VB.NET (Visual Studio 2008, .NET 3.5).

Я знаю, что «регулярные» (нестандартные) события на самом деле делегаты, поэтому я думал об использовании делегатов при реализации пользовательского события. С другой стороны, Эндрю ТроэльсС. "Pro VB 2008 и платформа .NET 3.5" Книга использует типы коллекций во всех его настраиваемых примерах событий, и Microsoft's Образец кодов соответствовать этой линии мысли.

Итак, мой вопрос: какие соображения я должен иметь при выборе одного дизайна над другим? Каковы плюсы и минусы для каждого дизайна? Что из них напоминает внутреннюю реализацию «регулярных» событий?

Ниже приведен образец код, демонстрирующий два дизайна.

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>. Отказ У вас также есть накладные расходы объектов и массивов и массивов, которые могут быть нулевыми, поэтому более влияние на сборку мусора и т. Д.

Кроме того, если вы делаете много этого рассмотреть EventHandlerList, что существует для обеспечения эффективного доступа к редкий События - то есть там, где вы хотите разоблачить много событий, но многие из них могут быть неназначены.

Первый пример намного ближе к «стандартным» событиям (хотя вы можете наблюдать за неназначенными / нулевыми обработчиками при звонке Invoke, как это может быть ноль). Кроме того, обратите внимание, что некоторые языки (честно говоря, не знаю, что здесь делает VB), применяет синхронизацию к событиям, но на самом деле очень мало событий В самом деле Необходимо быть в потоке, так что это может быть излишне.

редактировать Это также происходит, что есть функционал Разница между ними:

  • подход делегата рассматривает различные экземпляры с тем же целевым методом / экземпляром, что и равный (я не думаю, что List<T> буду)
  • Дублирующие делегаты: делегат удаляет в последний раз; Список удаляет самое раннее первое
  • Композиты: добавьте A, Add B и удаление (A + B) - с делегатом, это должно дать нулевое / пустое, но подход списка сохранится A и B (с помощью (с (A + B) удалить неисправность, чтобы ничего было найти)

Другие советы

Использование многокастерогата для удержания списка подписанных событий, безусловно, является работоспособным подходом, но не один, я особенно увлекаюсь. Чтобы добавить или удалить событие из многокастерегана, необходимо сделать одно из двух вещей:

  1. Приобретите замок, создайте новый делегат со старого, но с добавлением или удаленным событием, установите указатель делегата на этот делегат, а затем отпустите замок
  2. Скопируйте ссылку на старый делегат, создайте из него новый делегат, но с добавленным событием или удаленным событием используйте Interlocked.compareexchange, чтобы сохранить ссылку на новый, если старый не изменился, и начните по всему, если сравниватьxchange не удалось Отказ

Последний подход может предложить слегка лучшую производительность, если нет конфорации, но может работать плохо, если многие потоки одновременно пытаются добавлять или удалять события. Однако одно преимущество последнего подхода, заключается в том, что нет опасности умирающего потока, удерживая замок.

Ни один из подходов не кажется особенно чистым. Если кто-то планирует при вызове всех событий, просто вызвав делегат, производительность вызова событий может компенсировать производительность добавления / удаления. С другой стороны, если кто-то планирует использовать GetInvocationList, чтобы обернуть вызовы событий в блоках TRY / CATH, можно лучше просто использовать (соответственно заблокированный) список или другой структуру данных.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top