tag:blogger.com,1999:blog-8596733192274108952.post2679474091567550797..comments2024-03-12T06:00:18.305+02:00Comments on Programming stuff: Элегантная реализация «слабых событий»Sergey Teplyakovhttp://www.blogger.com/profile/14300835272589262297noreply@blogger.comBlogger13125tag:blogger.com,1999:blog-8596733192274108952.post-72363438462283665352015-09-26T21:27:27.519+03:002015-09-26T21:27:27.519+03:00Я не совсем понял, в каком именно месте INotifyPro...Я не совсем понял, в каком именно месте INotifyPropertyChanged.PropertyChanged наршуает гайдлайны? Там используется свой собственный делегат PropertyChangedEventHandler, но PropertyChangedEventArgs наследуют от EventArgs.<br /><br />Вообще-то ведь нет гайдлайнов, которые бы говорили, что НУЖНО обязательно использовать класс EventHandler. Каждый вправе объявлять свой тип делегатов, если они следуют соглашению: первый параметр object sender, а второй - наследник от EventArgs-ов.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-39969177490681599542015-09-26T16:44:58.164+03:002015-09-26T16:44:58.164+03:00С этим подходом есть небольшая проблема. Есть люди...С этим подходом есть небольшая проблема. Есть люди, которые guidelines то ли не читают, то ли сознательно по каким-то причинам не следуют, и объявляют события не типа EventHandler where T : EventArgs. Классический пример из кода Майкрософт - INotifyPropertyChanged.PropertyChanged. А также весь WPF. С такими событиями приведенный подход работать, к сожалению, не будет.Ivan Danilovhttps://www.blogger.com/profile/17733952955836067788noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-79079737417281177052015-09-26T16:42:10.312+03:002015-09-26T16:42:10.312+03:00Рабочее, но есть подводный камень. Лямбда для отпи...Рабочее, но есть подводный камень. Лямбда для отписки обычно делает capture долгоживущего event provider'а. В данном случае это синглтон, и такой опасности нет, но чаще это все же какой-то сервис или что-то подобное. Так вот, если вместе с лямбдой отписки в одном scope присутствует другая лямбда, которая делает capture того короткоживущего, что мы подписываем - получится implicit lambda capture (я, правда, не в курсе, может быть Roslyn делает несколько объектов capture уже, и эта проблема решена?), и наша подписка станет вполне себе сильной, а не weak. Еще больше проблему усугубляет, что это _крайне_ просто сделать случайно, и не заметить. А найти потом через год в профайлере, что память отжирается, и не освобождается.Ivan Danilovhttps://www.blogger.com/profile/17733952955836067788noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-5078684747831110542015-09-25T15:33:19.191+03:002015-09-25T15:33:19.191+03:00Я думаю имелось в виду то, что даже после занулени...Я думаю имелось в виду то, что даже после зануления ссылки на ShortLivedEventHandler события все равно будут хэндлится, пока не отработает GC. А т.к. вместо ручного вызова GC.Collect лучше полагаться на CLR, то сработает ли хэндлер или нет зависит от того, успеет ли сработать GC до вызова RaiseEvent...Anonymoushttps://www.blogger.com/profile/00602572651536004222noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-71360199425553037552015-09-24T08:50:33.439+03:002015-09-24T08:50:33.439+03:00Но ведь в этом и смысл слабого события. Чтобы ниче...Но ведь в этом и смысл слабого события. Чтобы ничего не происходило, если подписанный объект недоступен и собран сборщиком. Если такое поведение не нужно, то просто используйте обычные события:))Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-30189097498819433002015-09-24T08:40:09.278+03:002015-09-24T08:40:09.278+03:00Получается, не очевидно.
Т.е. выполнение обработчи...Получается, не очевидно.<br />Т.е. выполнение обработчика зависит от сборщика мусора.<br /><br />LongLivedEventProvider.Instance.RaiseEvent(); // handler отработает <br />GC.Collect();<br />LongLivedEventProvider.Instance.RaiseEvent(); // ничего не произойдетAnonymoushttps://www.blogger.com/profile/08040197198389664236noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-38205809202136815892015-09-23T19:57:50.721+03:002015-09-23T19:57:50.721+03:00Наличие отладчика продлевает время жизни переменны...Наличие отладчика продлевает время жизни переменных до конца метода (как минимум), чтобы не было сюрпризов, что остановился на последней строке, а локальная переменная уже дохлая.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-9137055201368418682015-09-23T19:46:07.367+03:002015-09-23T19:46:07.367+03:00Запуск без отладчика помог. Спасибо.Запуск без отладчика помог. Спасибо.maslbl4https://www.blogger.com/profile/08967409930959296618noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-11948176457640711922015-09-23T19:42:16.982+03:002015-09-23T19:42:16.982+03:00Да, вполне себе решение. Да, вполне себе решение. Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-40350950963016959182015-09-23T19:40:09.403+03:002015-09-23T19:40:09.403+03:00Только что проверил. Все работае.
Напомню, что ну...Только что проверил. Все работае.<br /><br />Напомню, что нужно запускать в Release-е и через Ctrl + F5 (без отладчика - Debug -> Start Without Debugger).<br /><br />И тогда все заработает.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-89206948677775728232015-09-23T18:59:24.329+03:002015-09-23T18:59:24.329+03:00К сожалению данный метод не работает если код ском...К сожалению данный метод не работает если код скомпилировать в VS 2013 <br />firstWeakReference.IsAlive возвращает True maslbl4https://www.blogger.com/profile/08967409930959296618noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-67747711986702670892015-09-23T13:00:25.569+03:002015-09-23T13:00:25.569+03:00Полезная информация. Спасибо. Но, имхо, "чест...Полезная информация. Спасибо. Но, имхо, "честнее" и правильней отписываться от событий. Anonymoushttps://www.blogger.com/profile/13714350909295009550noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-37476344445748715292015-09-23T12:56:23.315+03:002015-09-23T12:56:23.315+03:00>> при «зажигании» события, долгоживущему об...>> при «зажигании» события, долгоживущему объекту все равно придется перебирать и вызвать все обработчики, что также скажется на производительности, если приложение работает 24/7.<br /><br />Навскидку можно проблему решить так:<br />internal static class WeakEventHandler<br />{<br /> public static EventHandler Create(<br /> THandler handler, Action invoker, <br />Action> unsubscribe) // доп. параметр<br /> where THandler : class<br /> {<br /> var weakEventHandler = new WeakReference(handler);<br /> <br /> EventHandler result = null;<br /> result = (sender, args) =><br /> {<br /> THandler thandler;<br /> if (weakEventHandler.TryGetTarget(out thandler))<br /> {<br /> invoker(thandler, sender, args);<br /> } <br /> else<br /> {<br /> unsubscribe(result); //!!!!!!!!!!!!!!<br /> }<br /> };<br /> return result;<br /> }<br />}<br />var handler = WeakEventHandler.Create(this, <br /> (@this, o, args) => @this.EventHandler(), <br /> h => LongLivedEventProvider.Instance.Event -= h);<br />LongLivedEventProvider.Instance.Event += handler;<br /><br />jack128https://www.blogger.com/profile/06241586079811572654noreply@blogger.com