WeakEventDelegate реализация - запрос обратной связи - PullRequest
5 голосов
/ 04 июля 2011

Я только что реализовал WeakEventDelegate класс в .NET.

Я видел другие статьи о том, как реализовать такую ​​вещь в http://code.logos.com/blog/2008/08/event_subscription_using_weak_references.html и http://blogs.msdn.com/b/greg_schechter/archive/2004/05/27/143605.aspx

Однако реализация, к которой я пришел, менее сложна (хотя и менее гибка) и, кажется, делает свою работу, поэтому мне было интересно, пропустил ли я что-то.

Есть ли проблемы со следующей реализацией, за исключением относительной недостаточной гибкости?

public class WeakEventDelegate<TEventArgs> 
    where TEventArgs : EventArgs
{
    private readonly WeakReference handlerReference;

    public WeakEventDelegate(Action<object, TEventArgs> handler)
    {
        handlerReference = new WeakReference(handler);
    }

    public void Handle(object source, TEventArgs e)
    {
        Action<object, TEventArgs> unwrappedHandler = (Action<object, TEventArgs>)handlerReference.Target;
        if (unwrappedHandler != null)
        {
            unwrappedHandler.Invoke(source, e);
        }
    }
}

РЕДАКТИРОВАТЬ : Мое единственное намерение при написании этого класса состояло в том, чтобы предотвратить неявную ссылку издателя на делегата, чтобы предотвратить сборщик мусора подписчиком.

То есть вместо записи:

void subscribe()
{
    publisher.RaiseCustomEvent += this.HandleCustomEvent;
}

Я бы написал:

private readonly WeakDelegate<CustomEventArgs> _customHandler = new WeakDelegate<CustomEventArgs>(this.HandleCustomEvent);
void subscribe()
{
    publisher.RaiseCustomEvent += _customHandler.Handle;
}

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

Ответы [ 4 ]

3 голосов
/ 04 июля 2011

Основная проблема здесь заключается в том, что все, что подписано на вашего делегата (handlerReference.Target), сохранит handlerReference в живых.

Заключение этого предоставляет вам средства для вызова вашего делегата без ссылки.к этому, но не делает ничего, чтобы помешать подписчику сохранить ссылку.

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

0 голосов
/ 10 июня 2013

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

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

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

0 голосов
/ 07 июня 2013

Мысли

Мне нравится идея реализации слабого делегата на стороне класса, который обрабатывает событие, потому что я не хочу иметь слабые ссылки только в некоторых случаях. Для моей личной структуры пользовательского интерфейса я иногда реагирую на события, не имея ссылки на исходный объект:

void HandlePropertyChanged(Object sender, PropertyChangedArgs args)
{
    // Do some stuff with the sender
}

Для этого требуются неслабые делегаты, в то время как для других объектов требуются слабые.

Решение

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

// Create a weak reference to the target
_Sender = new WeakReference(target.Target);
_CallDelegate = (Action<TThis, TSender, TArgs>)Delegate.CreateDelegate(CallDelegateType, target.Method);

Это прекрасно работает, но осталась одна большая проблема: Как удалить событие, когда оно больше не используется?

Ну, для этого тоже есть решение, но решение требует некоторого кода отражения и является источником ошибок:

_RemoveMethod = (Action<DataEventHandler<TSender, TArgs>>)Delegate.CreateDelegate(AddMethodType, eventHolder, eventDeclaration.GetRemoveMethod());

eventDeclaration является экземпляром EventInfo и поставляется с аргументами конструктора, чтобы сделать его немного проще для пользователя. Я создал второй конструктор, который просто принимает экземпляр (eventHolder) и строку, которая представляет название события.

Когда объект больше не жив, вызывается _RemoveMethod -делегат, который удаляет WeakDelegate.

Использование

n.Disposing += (_DisposeDelegate = new WeakDelegate<Drawable, IComponent,Object>(n, "Disposing", HandleRootDisposing));

Как вы можете видеть, есть еще одна проблема: WeakDelegate должен храниться в памяти, когда требуется удалить событие позже. Для этого тоже есть решение, но оно требует переопределения методов равенства WeakDelegate и сохранения всех делегатов в хеш-таблице, что может сильно замедлить работу приложения.

0 голосов
/ 06 июля 2011

Я обнаружил критическую проблему с этой реализацией - она ​​работает, только если я сохраняю ссылку на делегат (обработчик, переданный в конструкторе) где-то еще.В противном случае этот объект делегата собирается, и событие никогда не запускается снова.

...