_NN>>>Тут ведь получается, что нужно хранить этот 'MyHandler' только для того, чтобы отписаться от события. _NN>>>Т.е. его нельзя сделать анонимной функцией, локальной переменной и т.д.
KAR>>Не нужно ничего хранить, это глобальная константа. На ивент же может быть больше одного листенера... _NN>Как не нужно ? _NN>Вот в этом примере как отписаться ? _NN>
_NN>Тут ведь получается, что нужно хранить этот 'MyHandler' только для того, чтобы отписаться от события. _NN>Т.е. его нельзя сделать анонимной функцией, локальной переменной и т.д.
Не нужно ничего хранить, это глобальная константа. На ивент же может быть больше одного листенера...
Тут ведь получается, что нужно хранить этот 'MyHandler' только для того, чтобы отписаться от события.
Т.е. его нельзя сделать анонимной функцией, локальной переменной и т.д.
Эти мысли привели к более разумному применению как например в Rx, где подписка возвращает объект через который можно отписаться.
Здравствуйте, os24ever, Вы писали:
_NN>>как например в Rx O>Погуглил и нашёл: Just say No to “Reactive Programming”. O>Автор пишет, что это всего лишь Observer/Observable и был описан в книге GoF много лет тому назад. O>Потому, наверное, так и сделано: "согласно положениям GoF".
Здравствуйте, os24ever, Вы писали: O>Автор пишет, что это всего лишь Observer/Observable и был описан в книге GoF много лет тому назад. O>Потому, наверное, так и сделано: "согласно положениям GoF".
Автор — поверхностный хам и воинствующее невежество. Он нашёл в Rx знакомое слово и из этого раздул целую статью.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
_NN>>Тут ведь получается, что нужно хранить этот 'MyHandler' только для того, чтобы отписаться от события. _NN>>Т.е. его нельзя сделать анонимной функцией, локальной переменной и т.д.
KAR>Не нужно ничего хранить, это глобальная константа. На ивент же может быть больше одного листенера...
Как не нужно ?
Вот в этом примере как отписаться ?
window.addEventListener('click', function() {} );
Вот классическая ошибка первого варианта в JavaScript:
function f() {
var handler = function() {
}
// Удалить старое если было
window.removeEventListener('click', f);
// Добавить новое
window.addEventListener('click', f);
}
Тут каждый вызов функции создается другая переменная и в итоге ничего не удаляется.
Второй вариант также позволяет иметь более одного подписчика.
Здравствуйте, _NN_, Вы писали:
_NN>Кто же все таки посчитал, что первый подход лучше ?
Я никогда не слышал, чтобы при дизайне C# 1.0 рассматривались другие варианты (анонимных функций тогда не было). Так что вполне возможно, что никто не решал, что выбранный подход лучше, это могло быть единственное решение, которое тогда пришло в голову дизайнерам. Хранить анонимные функции, конечно, неудобно, поэтому в Rx придумали другой подход. Но тебе всё равно нужно хранить объект, у которого ты собираешься вызывать Dispose() для отписки.
Здравствуйте, nikov, Вы писали:
N>Здравствуйте, _NN_, Вы писали:
_NN>>Кто же все таки посчитал, что первый подход лучше ?
N>Я никогда не слышал, чтобы при дизайне C# 1.0 рассматривались другие варианты (анонимных функций тогда не было). Так что вполне возможно, что никто не решал, что выбранный подход лучше, это могло быть единственное решение, которое тогда пришло в голову дизайнерам. Хранить анонимные функции, конечно, неудобно, поэтому в Rx придумали другой подход. Но тебе всё равно нужно хранить объект, у которого ты собираешься вызывать Dispose() для отписки.
В любом случае нужен какой-нибудь идентификатор для отписки.
Другое дело, что им почему-то является сам метод.
В таком варианте не всегда возможно подписаться в одном месте, а отписаться в другом.
Как единственное решение ? На WinAPI не смотрели что ли
Образный код:
auto timer = SetTimer(myCallback);
KillTimer(timer);
Здравствуйте, _NN_, Вы писали:
_NN>В любом случае нужен какой-нибудь идентификатор для отписки. _NN>Другое дело, что им почему-то является сам метод. _NN>В таком варианте не всегда возможно подписаться в одном месте, а отписаться в другом.
_NN>Как единственное решение ? На WinAPI не смотрели что ли _NN>Образный код: _NN>
_NN>Тут ведь получается, что нужно хранить этот 'MyHandler' только для того, чтобы отписаться от события. _NN>Т.е. его нельзя сделать анонимной функцией, локальной переменной и т.д.
_NN>Эти мысли привели к более разумному применению как например в Rx, где подписка возвращает объект через который можно отписаться. _NN>
наоборот сделать уже сложнее, поэтому лучше использовать +=/-= с которыми можно использовать обе схемы,
чем использовать Subscribe с которым уже не сделать -=.
Здравствуйте, Abyx, Вы писали:
A>наоборот сделать уже сложнее, поэтому лучше использовать +=/-= с которыми можно использовать обе схемы, A>чем использовать Subscribe с которым уже не сделать -=.
Что значит сложнее ?
Да и проблема то какая решается ?
subscription1 = event.Subscribe(func1)
subscription2 = event.Subscribe(func2)
subscription2.Dispose(); // Отписались от 2
subscription1.Dispose(); // Отписались от 1
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Abyx, Вы писали:
A>>наоборот сделать уже сложнее, поэтому лучше использовать +=/-= с которыми можно использовать обе схемы, A>>чем использовать Subscribe с которым уже не сделать -=.
_NN>Что значит сложнее ? _NN>Да и проблема то какая решается ?
_NN>
_NN>subscription1 = event.Subscribe(func1)
_NN>subscription2 = event.Subscribe(func2)
_NN>subscription2.Dispose(); // Отписались от 2
_NN>subscription1.Dispose(); // Отписались от 1
_NN>
Здравствуйте, Abyx, Вы писали:
A>и кто и где должен хранить subscription?
Кто подписывается тот и хранит или передает дальше куда надо.
Разработчик волен делать что хочет.
С другой стороны не нужно знать про сам метод, чтобы подписаться.
Можно подписываясь передать приватный метод или анонимный, и отписаться можно извне без проблем.
_NN>как например в Rx
Погуглил и нашёл: Just say No to “Reactive Programming”.
Автор пишет, что это всего лишь Observer/Observable и был описан в книге GoF много лет тому назад.
Потому, наверное, так и сделано: "согласно положениям GoF".
Здравствуйте, _NN_, Вы писали: _NN>Можно подписываясь передать приватный метод или анонимный, и отписаться можно извне без проблем.
По-прежнему не очень понятно, в чём принципиальная разница между:
Удобнее тем, что внешнему коду не нужно знать конкретный тип обработчика, чтобы отменить операцию. Это значит, что мы можем использовать обобщённую логику отписки для разных "событий".
К тому же IDisposable можно комбинировать:
var disposable1 = stream.Subscribe(...);
var disposable2 = Disposable.Create(() => ...);
return new CompositeDisposable() { disposable1, disposable2 } //Возвращает IDisposable, который по очереди вызовет disposable1 и disposable2
Здравствуйте, ionoy, Вы писали:
I>Удобнее тем, что внешнему коду не нужно знать конкретный тип обработчика, чтобы отменить операцию.
Что такое "конкретный тип обработчика"?
I>Это значит, что мы можем использовать обобщённую логику отписки для разных "событий".
Пример кода, который вы хотите написать, в студию. С пояснением, почему вы не можете его написать в нынешнем коде.
I>К тому же IDisposable можно комбинировать:
I>var disposable1 = stream.Subscribe(...); I>var disposable2 = Disposable.Create(() => ...);
I>return new CompositeDisposable() { disposable1, disposable2 } //Возвращает IDisposable, который по очереди вызовет disposable1 и disposable2 I>[/c#]
Непонятно, почему именно Disposable.
Почему вы не хотите комбинировать, например, Action.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Пример кода, который вы хотите написать, в студию. С пояснением, почему вы не можете его написать в нынешнем коде.
Встяну. Вот лично у меня присутствует чисто психологический дискомфорт сохранять где-либо для последующей отписки ссылку на функцию. Оно конечно это можно списать на фантомные боли императивщика, но — а вдруг это замыкание какое, к примеру, тянущее за собой чёрт знает сколько захваченных данных?
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, ionoy, Вы писали:
I>>Удобнее тем, что внешнему коду не нужно знать конкретный тип обработчика, чтобы отменить операцию. S>Что такое "конкретный тип обработчика"?
Потому что источники событий могут быть разные (Event1, Event2 и любая другая подписка).
Получается, что методу отписки нужно знать не только тип обработчика, но и само событие от которого нужно отписаться. Далеко не всегда есть возможность эту информацию предоставить.
S>Непонятно, почему именно Disposable. S>Почему вы не хотите комбинировать, например, Action.
Из-за разных наборов параметров.
Здравствуйте, ionoy, Вы писали:
S>>Непонятно, почему именно Disposable. S>>Почему вы не хотите комбинировать, например, Action. I>Из-за разных наборов параметров.
Дополню:
И необходимости передавать источник. От самого Action толку мало, в отличие от IDisposable.
Здравствуйте, ionoy, Вы писали:
I>Дополню: I>И необходимости передавать источник. От самого Action толку мало, в отличие от IDisposable.
А, я, наверное, непонятно выразился.
вот что я имел в виду:
Как видим, внешнему коду вообще не нужно знать ни тип обработчика, ни конкретное событие. Достаточно позвать делегат, который ему вернули, в тот момент, когда вы хотели вызывать IDisposable.Dispose.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S>Как видим, внешнему коду вообще не нужно знать ни тип обработчика, ни конкретное событие. Достаточно позвать делегат, который ему вернули, в тот момент, когда вы хотели вызывать IDisposable.Dispose.
Примерно так оно и работает внутри. Есть, например, удобный метод:
Как я это вижу, у IDisposable есть несколько преимуществ перед Action:
1. На основании этого интерфейса можно создать готовую библиотеку реализаций IDisposable, как сделали в Rx. Такие объекты предоставляют дополнительную функциональность для кода выдающего подписку, а для клиентов всё тот же IDisposable. Про некоторые готовые реализации опять же здесь: http://synchronicity-life.blogspot.com/2011/12/idisposable-implementations-in-reactive.html
2. IDisposable — это распространённый шаблон проектирования. Иногда бывает удобно локализовать подписку с помощью using.
3. IDisposable семантически лучше подходит на роль отписчика, чем Action
I>Как я это вижу, у IDisposable есть несколько преимуществ перед Action:
Ну так и используйте его, если в вашей инфраструктуре он удобнее.
Лично мне более удобными кажутся weak events, т.к. сама необходимость следить за отпиской напрягает. Независимо от того, используется ли "честный" OnClick -=, Action, или IDisposable.
Я очень часто сталкивался с тем, что нам нужен "observer, который следит, покуда жив", а не "observer, который жив, покуда следит".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
I>>Как я это вижу, у IDisposable есть несколько преимуществ перед Action: S>Ну так и используйте его, если в вашей инфраструктуре он удобнее. S>Лично мне более удобными кажутся weak events, т.к. сама необходимость следить за отпиской напрягает. Независимо от того, используется ли "честный" OnClick -=, Action, или IDisposable. S>Я очень часто сталкивался с тем, что нам нужен "observer, который следит, покуда жив", а не "observer, который жив, покуда следит".
Многие Observable так и устроены.
Например, такой код:
stream.Take(3).Subscribe(x => DoSomething(x))
сам отпишет подписчика. IDisposable нужен только в тех случаях, когда ты сам хочешь выбирать момент где переставать следить за потоком.
Здравствуйте, ionoy, Вы писали: I>сам отпишет подписчика.
Это понятно. Есть обратные ситуации — datasource.OnChange += form1.Refresh навсегда удержит форму в памяти.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, ionoy, Вы писали: I>>сам отпишет подписчика. S>Это понятно. Есть обратные ситуации — datasource.OnChange += form1.Refresh навсегда удержит форму в памяти.
Так ведь можно отписаться по необходимости. Или я чего-то не понимаю?
Здравствуйте, ionoy, Вы писали: S>>Это понятно. Есть обратные ситуации — datasource.OnChange += form1.Refresh навсегда удержит форму в памяти. I>Так ведь можно отписаться по необходимости. Или я чего-то не понимаю?
Что значит "по необходимости"? Как вы определите момент необходимости?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, ionoy, Вы писали: S>>>Это понятно. Есть обратные ситуации — datasource.OnChange += form1.Refresh навсегда удержит форму в памяти. I>>Так ведь можно отписаться по необходимости. Или я чего-то не понимаю? S>Что значит "по необходимости"? Как вы определите момент необходимости?
Вопрос был в том, что привязка навсегда удержит форму в памяти. Если есть такая проблема, значит когда-то эта форма перестаёт быть нужной? Обычно момент удаления формы более или менее детерминирован.
Ничего не понял. По ссылке — абсолютно работоспособный пример. На событие подписывается ровно одна функция.
В этом коде — тоже. На какой вопрос вы отвечаете — непонятно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, ionoy, Вы писали: I>Вопрос был в том, что привязка навсегда удержит форму в памяти. Если есть такая проблема, значит когда-то эта форма перестаёт быть нужной?
Обычно (в среде с недетерминистической финализацией) объект перестаёт быть нужен тогда, когда последняя ссылка на него выходит из области видимости.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, ionoy, Вы писали: I>>Вопрос был в том, что привязка навсегда удержит форму в памяти. Если есть такая проблема, значит когда-то эта форма перестаёт быть нужной? S>Обычно (в среде с недетерминистической финализацией) объект перестаёт быть нужен тогда, когда последняя ссылка на него выходит из области видимости.
Ссылка сама по себе не выходит из области видимости, какой-то код управляет этим процессом, в любом случае. Может просто пример с формой неудачный, т.к. там нет проблем отследить закрытие
Здравствуйте, ionoy, Вы писали:
I>Ссылка сама по себе не выходит из области видимости, какой-то код управляет этим процессом, в любом случае.
Да ладно!
Ещё как выходит. Если бы это было не так, то нам вообще бы не потребовался сборщик мусора — мы всегда бы знали, в какой момент пора звать деструктор и освобождать память
I>Может просто пример с формой неудачный, т.к. там нет проблем отследить закрытие
Да. С формой пример не совсем удачный, т.к. в ней есть явный Dispose, в котором можно сделать отписку и полагаться на компонентную модель в контроле времени жизни. И тем не менее — ошибку типа "забыл отписаться" сделать очень легко. Эта возможность граблей никуда не денется от того, что оператор += будет возвращать что-то другое. Нам всё равно нужно куда-то записывать ссылку, либо на IDisposable, который надо не забыть отдиспозить, либо на хэндлер, который нужно отписать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, ionoy, Вы писали:
I>>Ссылка сама по себе не выходит из области видимости, какой-то код управляет этим процессом, в любом случае. S>Да ладно! S>Ещё как выходит. Если бы это было не так, то нам вообще бы не потребовался сборщик мусора — мы всегда бы знали, в какой момент пора звать деструктор и освобождать память
А можно пример, где ссылка сама по себе выходит из области видимости, но при этом остаётся в памяти из-за привязки к событию?
I>>Может просто пример с формой неудачный, т.к. там нет проблем отследить закрытие S>Да. С формой пример не совсем удачный, т.к. в ней есть явный Dispose, в котором можно сделать отписку и полагаться на компонентную модель в контроле времени жизни. И тем не менее — ошибку типа "забыл отписаться" сделать очень легко. Эта возможность граблей никуда не денется от того, что оператор += будет возвращать что-то другое. Нам всё равно нужно куда-то записывать ссылку, либо на IDisposable, который надо не забыть отдиспозить, либо на хэндлер, который нужно отписать.
Это верно, ошибиться можно. Другое дело, что про эти грабли разве что глухой не слышал.
т.к. внутри себя объект "подписки" будет вызывать Unsubscribe. Оригинальный myHandler все равно где-то должен храниться.
Т.е. это не более чем синтаксический сахар, причем не самый удобный. Если хочется, его можно реализовать быстро, но фишка в том, что смысла особого нет.
Вариант с объектами-подписками означает, что подписки надо где-то хранить. Это автоматически влечет проблемы с подписчиками, желающими остаться инвариантами и/или подписываемые к более чем одному источнику. Причем, не стоит забывать, что очень часто, если не сказать "обычно", подписчик даже и не знает, что его подписали-отписали. В таких случаях явный