tag:blogger.com,1999:blog-8596733192274108952.post5817047908276380018..comments2024-03-12T06:00:18.305+02:00Comments on Programming stuff: О явном вызове метода DisposeSergey Teplyakovhttp://www.blogger.com/profile/14300835272589262297noreply@blogger.comBlogger23125tag:blogger.com,1999:blog-8596733192274108952.post-52556108687432484862013-08-13T22:10:30.172+03:002013-08-13T22:10:30.172+03:00Все меняется если приложение использует ресурсоемк...Все меняется если приложение использует ресурсоемкие неуправляемые объеты.<br />Эксперименты показали, что GC достаточно милостиво относится к ним (даже если явно указывать давление на память и т.п.), а это достаточно неприятно если один такой объект может отжирать под 300метров и загружать под 25-30 процентов процессора.<br />GC поступает с ними как с любым объектом в старшем поколении - оставляет жить на долго не вызывая деструктора.<br />С одной стороны вьюмодели которые изначально без диспоуза с другой - где-то в глубине у них появляются такие вот монстры.<br />И тут приходится полностью забывать про автоматическое управление ресурсами и по всему дереву вьюмоделей внедрять диспоуз с обязательным использованием.<br />Хотя может есть иные - более остроумные способы, но я не нашел ничего.Anonymoushttps://www.blogger.com/profile/03999939535124861861noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-10833735100122195052013-08-13T19:00:11.350+03:002013-08-13T19:00:11.350+03:00"Для чего кто-то делает Disposable-классы? Дл...<i>"Для чего кто-то делает Disposable-классы? Для того, чтобы сказать своим пользователям, что у него есть способ разрушить свое состояние, когда в нем отпадет необходимость. Правильно спроектированный класс будет реализовывать интерфейс Disposable только в том случае, когда его поведение согласуется с семантикой реализуемого интерфейса.<br /><br />Если мы с вами сейчас говорим за вменяемый дизайн и реализацию, то в этом случае наличие интерфейса IDisposable должно говорить нам, что с этим классом нужно работать особым образом.<br /><br />Разработчики классов и его пользователи могут не следовать этим советам и именно это делает нашу жизнь несколько сложнее. Какой подход по умолчанию выбрать - решать каждому из нас самостоятельно, но когда говорится о дизайне и общих советах, то тут следует отталкиваться от наиболее вероятных сценариев поведения. "</i> - именно так. По-моему этим всё сказано, не вижу больше причин для продолжения дискусси.Karen Tazayanhttps://www.blogger.com/profile/17340270363445850237noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-14156254342501369322013-08-13T17:05:40.814+03:002013-08-13T17:05:40.814+03:00Хотя прошлое мое утверждение спорно...Хотя прошлое мое утверждение спорно...Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-12003258199586660082013-08-13T17:01:15.211+03:002013-08-13T17:01:15.211+03:00Правильно спроектированный класс будет реализовыва...<i>Правильно спроектированный класс будет реализовывать интерфейс Disposable только в том случае, когда его поведение согласуется с семантикой реализуемого интерфейса. </i><br /><br />В случае с FileStream, как мне кажется, не совсем логично предположить что в Dispose будет происходить Close.Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-48765985105803043172013-08-13T16:56:30.720+03:002013-08-13T16:56:30.720+03:00Сергей, я не говорю что использовать IDisposable н...Сергей, я не говорю что использовать IDisposable не надо. Я говорю о том, что нужно понимать что делает Dispose. И вызывать лишь тогда, когда это нужно. Но это не должно быть обязательным. Мы все понимаем что надо обязательно закрывать FileStream. Но не обязательно вызывать Dispose. Понимаете о чем я хочу сказать? Это другая семантика, это другая задача. В .net нет объектов, которым обязательно надо вызывать Dispose. НЕ верно проектировать класс в надежде что Dispose вызовут. Более верно сделать логичное использование объекта, как в случае с FileStream (открыл/закрыл) и просто добавить возможность использовать объект в using, для того, что бы подсластить жизнь пользователей. И написать что close будет происходить в Dispose.<br /><br />Более того т.к. в Dispose можно сделать очень многое и каждый может сделать все что угодно, нельзя вызывать Dispose бездумно. Не все проектируют верно, и вы не можете сказать точно что сделает Dispose и точно ли объекты которые используются в моей библиотеки не будут использовать диспозейбленный объект. Поэтому, я использую Dispose лишь тогда когда об этом говорит документация библиотеки (объекта), когда я знаю что произойдет и у меня есть на то причины.<br />Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-13908634945701999802013-08-13T16:33:14.666+03:002013-08-13T16:33:14.666+03:00Ок.
У камрада Скотта Мейерса есть одно любимое выр...Ок.<br />У камрада Скотта Мейерса есть одно любимое выражение: классом должно быть легко пользоваться правильно и сложно пользоваться неправильно. <br /><br />И второе: дизайн класса должен передавать намерения разработчика. <br /><br />Для чего кто-то делает Disposable-классы? Для того, чтобы сказать своим пользователям, что у него есть способ разрушить свое состояние, когда в нем отпадет необходимость. Правильно спроектированный класс будет реализовывать интерфейс Disposable только в том случае, когда его поведение согласуется с семантикой реализуемого интерфейса. <br /><br />Если мы с вами сейчас говорим за вменяемый дизайн и реализацию, то в этом случае наличие интерфейса IDisposable должно говорить нам, что с этим классом нужно работать особым образом. <br /><br />Разработчики классов и его пользователи могут не следовать этим советам и именно это делает нашу жизнь несколько сложнее. Какой подход по умолчанию выбрать - решать каждому из нас самостоятельно, но когда говорится о дизайне и общих советах, то тут следует отталкиваться от наиболее вероятных сценариев поведения. <br /><br />Мой опыт говорит, что наличие вызова Dispose мне еще никогда не навредило, а вот благодаря отсутствию вызова, я сталкивался с проблемами. Этого же подхода придерживается товарищ Джо Даффи и авторы Framework Design Guidelines. Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-84149535958433811942013-08-13T16:21:50.043+03:002013-08-13T16:21:50.043+03:00А спор о том, что вызывать Dispose надо только тог...А спор о том, что вызывать Dispose надо только тогда, когда тебе это надо и ты знаешь что там происходит и зачем. Но не:<br /><br /><i> что вызов метода Dispose жизненно необходим, пока не доказано обратное.</i>Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-20621477128445315562013-08-13T16:17:36.931+03:002013-08-13T16:17:36.931+03:00Вадим, деструктор в С++ - это тоже обычный метод, ...Вадим, деструктор в С++ - это тоже обычный метод, если я создам объект в куче, и затем его не удалю, то он не вызовется. На некотором низком уровне все является методом. Но у разных методов разный смысл. <br /><br />Конструктор ведь это тоже обычный метод, но именно там мы стараемся инициализировать инвариант класса. Но и он тоже не всегда вызывается, но это же не делает его бесполезным!Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-24603707303495489152013-08-13T16:14:47.662+03:002013-08-13T16:14:47.662+03:00ИМХО, минипрофайл построен на основе идиомы RAII, ...ИМХО, минипрофайл построен на основе идиомы RAII, которая обеспечивает выполнение некоторого действия по выходу из области видимости. Да, ресурсов здесь нет (никаких), но ведь я приводил аналогичные примеры в пункте 3. Использование Disposable объектов не для управления ресурсами. <br />Так в чем наше разногласие?<br />Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-83622410152435026042013-08-13T16:12:12.556+03:002013-08-13T16:12:12.556+03:00Вадим, почему вы считаете, что Dispose - это обычн...<i>Вадим, почему вы считаете, что Dispose - это обычный метод? Разработчики .NET, например, говорят что именно Dispose метод является ключевым способом освобождения ресурсов, а финализатор должен использоваться в виде страховочного троса. </i><br /><br />Да потому что это обычный метод по своей сути, с помощью которого реализуется интeрфейс IDisposable. :) Если я не вызову этот метод явно или используя using, его никто не вызовет. Поэтому если я использую неуправляемые ресурсы, то мне надо обязательно реализовывать финализатор и там либо дергать Dispose или освобождать ресурсы. Если объект реализует IDisposable, его можно использовать в using. Вот и все прелести.Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-92119113751851468122013-08-13T16:11:19.670+03:002013-08-13T16:11:19.670+03:00Вадим, тот факт, что кто-то использует что-то не п...Вадим, тот факт, что кто-то использует что-то не по назначению еще ни о чем не говорит. <br />Ведь весь сыр-бор из-за чего: я отталкиваюсь от предположения, что вызывать Dispose нужно, как можно раньше и это должно быть нашим поведением по умолчанию. У вас мнение другое; я его ценю, хотя и не понимаю. <br /><br />Я сам привел пару примеров, когда метод диспоз не разрушает состояние объекта, но тем не менее его вызов является необходимым. Если вы считает, что наиболее безопасно его не вызывать, что же, я возражать не буду. Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-20841403976862411522013-08-13T16:06:44.865+03:002013-08-13T16:06:44.865+03:00Вадим, почему вы считаете, что Dispose - это обычн...Вадим, почему вы считаете, что Dispose - это обычный метод? Разработчики .NET, например, говорят что именно Dispose метод является ключевым способом освобождения ресурсов, а финализатор должен использоваться в виде страховочного троса. <br />Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-42277065323302517442013-08-13T15:59:02.568+03:002013-08-13T15:59:02.568+03:00При этом семантика (смысл, роль) деструкторов и ме...<i>При этом семантика (смысл, роль) деструкторов и метода Dispose - совпадает: они оба предназначены для разрушения инварианта объекта.</i><br /><br />Что бы не писали в MSDN, семантика уже давно не совпадает. Возьмите Razor, возьмите логирование, возьмите MiniProfiler (http://miniprofiler.com/). Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-76588689309314789892013-08-13T15:52:48.747+03:002013-08-13T15:52:48.747+03:00Не верно использовать Dispose не зная что там и за...Не верно использовать Dispose не зная что там и зачем. <br /><br />Яркий пример тому отложенная загрузка из какого либо источника. Источник реализует IDisposable. После этого из источника вы получаете объект, который загрузит данные только по требованию. Вызов Dispose у источника до того, как вы обратились к объекту с отложенной загрузкой есть ошибка. А источником может быть что угодно. И спрятать я могу источник за другими IDisposable объектами, которые в итоге вызовут Dispose у источника. И вызов Dispose у объекта "обертки" по вашему мне надо вызывать всегда, не зная что там происходит? И таких примеров может быть куча.<br /><br />Вы ведь не можете быть уверены в том, что делает другой объект, только если вам явно не указали что он делает или если его написали не вы. И так как Dispose это такой же метод как и все, спрятать за ним можно все что угодно. Закрытие соединения, транзакции, разлочивание объекта. И делать все это вы должны только тогда, когда вам это нужно. Будь то даже работа с FileStream. Dispose нужен для удобства, что бы не забывать закрывать поток. Но вы это можете сделать и без Dispose, просто вызвав close. Т.е. обязательства вызывать Dispose нет. Более того, если бы я НЕ знал что там происходит, я бы не вызывал Dispose, т.к. название метода Dispose не дает мне ровным счетом никакого представления о том, что он делает. А вдруг он не закрывает FileStream, а уничтожает полностью файл? Ведь Dispose это не просто очистка памяти, это какое то действие, которое произойдет в момент вызова метода Dispose, или в момент выхода из оператора Using. <br /><br />И опять таки про FileStream, т.к. этот объект будет владеть неуправляемыми ресурсами, то у него реализован финализатор. Т.е. если не вызывать Dispose, то "великого зла" не произойдет.<br /><br />Для меня, реализация IDisposable, говорит лишь о том, что я могу этот объект использовать в using с удобством для себя(примеров может быть куча: транзакции, логирование, работа в рамках какого либо контекста, да даже профилирование). Но это не обязывает меня использовать Dispose. Это можно делать лишь тогда, когда мне это нужно и тогда когда я знаю что произойдет, когда меня об этом предупредили или объяснили что это обязательно.<br /><br /><i>И я не приравниваю диспоз с деструктором, я говорю о том, что если я делаю класс диспозабл, то самим дизайном класса я говорю о важности вызова этого метода.</i><br /><br />Очень спорно. А если это класс логгер, или профайлер, или генеация HTML? И реализация IDisposable только для возможности использовать синтаксический сахар? И вызов Dispose "очень сильно" не обязателен?<br /><br />Более того, ничего "криминального" не произойдет если ваш объект владеет управляемыми ресурсами. Другое дело неуправляемые ресурсы, но тут вы будете реализовывать финализатор, и опять таки Dispose можно вообще не реализовывать (хотя это будет не очень красиво).Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-20572640837497017962013-08-13T14:51:38.000+03:002013-08-13T14:51:38.000+03:00Вадим, существует принципиальная разница между дес...Вадим, существует принципиальная разница между деструктором в С++ и финализатором в .NET. Но разница эта заключается не в семантике этих двух зверей, а во времени вызова. <br /><br />Именно из-за недетерминированного вызова финализатора появился интерфейс IDisposable с дополнительными синтаксическими конструкциями типа блока using (кстати, именно эти конструкции являются синтаксическим сахаром, а не сам интерфейс IDisposable). При этом семантика (смысл, роль) деструкторов и метода Dispose - совпадает: они оба предназначены для разрушения инварианта объекта. Т.е. конструктор инвариант создает, затем мы пользуемся объектом, а деструктор или метод Dispose его разрушает. <br /><br />В пользу этой мысли может служить язык C++\CLI в котором есть такое понятие как dereferenced variable (и dereferenced field) - это переменные ссылочных типов, объявленные без ^. И ведут себя они так: при выходе из области видимости вызывается метод Dispose. <br />Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-42755852623574074592013-08-13T14:31:03.422+03:002013-08-13T14:31:03.422+03:00Вадим, Даффи и Рихтер говорят необ одном и том же....Вадим, Даффи и Рихтер говорят необ одном и том же. Даффи говорит об очевидности владения речурсами (мои первый и второй случай из статьи), а Рихтер говорит о том, что вы знаете, что эта очистка нужна. Это две совершенно разные вещи. <br /><br />И я не приравниваю диспоз с деструктором, я говорю о том, что если я делаю класс диспозабл, то самим дизайном класса я говорю о важности вызова этого метода. Если класс реализует этот интерфейс, то это автоматом говорит всем его клиентам, что вызывать метод надо (пока документация или опыт использования не говорит обратного). <br /><br />Рыть же исходники - это тоже не вариант, поскольку use cases класса определяются разработчиком класса, а не клиентом. И наиболее безопасный способ использования диспозабл объектов такой: считаем, что вызов метода Dispose жизненно необходим, пока не доказано обратное. Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-24990182591182803942013-08-13T14:23:42.915+03:002013-08-13T14:23:42.915+03:00И все таки, Рихтер и Даффи говорят одно и тоже.
...И все таки, Рихтер и Даффи говорят одно и тоже. <br /><br />Даффи: "Если тип реализует интерфейс IDisposable и владение ресурсами <b>очевидно</b>, вы должны сделать все возможное для вызова метода Dispose после завершения использования объекта"<br /><br />Рихтер: "Я рекомендую явно вызывать метод Dispose только тогда, когда <b>вы точно знаете</b>, что в этом месте требуется очистка ресурсов"<br /><br />Т.е. если ты уверен, то вызывай, не уверен не вызывай. Если вы не вызовите Dispose, это не страшно, об этом говорят оба.<br /><br />ДАффи: "Но если владение ресурсами <b>неочевидно</b> (поскольку объект используется во множестве мест или используется из нескольких потоков), то <b>отсутствие</b> вызова Dispose не причинит особого вреда."<br /><br />Так что это скорее вы приравниваете Dipose к деструктору, говоря что вызывать надо обязательно.Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-2197298083496290042013-08-13T14:07:24.415+03:002013-08-13T14:07:24.415+03:00В вашем примере я вызову Dispose если вы явно скаж...В вашем примере я вызову Dispose если вы явно скажете что после использования вашего объекта желательно освободить ресурсы, либо в Dispose закрывается транзакция, либо еще что-то. Возможно что я предположу что закрыть транзакцию или освободить ресурсы удобно в Dispose, посмотрю что вы делаете в Dispose и только после этого задумаюсь о использовании Dispose.<br /><br />Если мне не сказали что происходит в Dispose и что это удобно использовать,если объект не намекает на использование Dispose, я даже не посмотрю реализует ли объект IDisposable.<br /><br />И опять таки я соглашусь с Рихтером, это нужно использовать только тогда, когда это нужно вам, когда вы понимаете что происходит. Это не деструктор, который нужно вызывать для очистки памяти. Dispose это синтаксический сахар, который удобно использовать c Using. Это все равно что еще один метод, но это не деструктор. Деструктор надо вызывать всегда. Метод класса, нужно вызывать лишь тогда, когда это вам нужно, и когда вы понимаете что он делаете. Даффи же сводит Dispose к деструктору, что не верно. Замените метод Dispose на Close , CommitTransaction, EndTag и т.д. Получается что эти методы вы не будете стараться вызвать всегда, а лишь тогда когда вам это нужно. А ведь Dispose именно это и делает, просто теперь этот объект можно использовать в using, что есть синтаксический сахар и очень красиво, т.к. ограничивает область видимости и позволяет сборщику понимать что это больше не надо.Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-60473974948705036252013-08-13T12:21:35.643+03:002013-08-13T12:21:35.643+03:00Вадим, мне все зе кажется, что подход должен быть ...Вадим, мне все зе кажется, что подход должен быть противоположным. Мы должны вызывать Dispose всегда и не вызывать его лишь тогда, когда точно знаем, что вреда от этого не будет. <br /><br />В общем же случае, мы просто не знаем, будут или нет негативные последствия отсутствия вызова Dispose, поэтому мы должны приложить все усилия, чтобы вызвать этот метод. <br /><br />(Ведь случай, когда вызов Dispose вреден только один - объект еще кем-то используется; но в большинстве случаев мы знаем об этом. Именно в этом мое мнение расходится с мнением Рихтера, который считает, что очень часто мы этого не знаем!)<br /><br />Вт такой вопрос: я предоставил вам класс, реализующий интерфейс IDisposable; поведение этого метода - деталь реализации. Вы будете вызывать Dispose? Судя по вашим комментариям, вы этого делать не будете, я же считаю такой подход ошибочным, ведь это может привести к непредсказумым последствиям, включая утечку памяти и нсвоевременное освобождение ресурсов, которое приведет к трудноуловимым ошибкам.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-2379540977718396042013-08-13T11:24:27.716+03:002013-08-13T11:24:27.716+03:00Ммм... Видимо мы с вами воспринимаем текст через п...Ммм... Видимо мы с вами воспринимаем текст через призму своего Я. <br /><br />Я согласен с тем, что Dispose нужно вызывать только в том случае, если вы знаете что произойдет. Во всех остальных случая делать этого не стоит. Будь то закрытие транзакции, освобождение блокировок или просто очистка ресурсов.Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-45209090105850354032013-08-13T11:00:48.080+03:002013-08-13T11:00:48.080+03:00Позвольте небольшое дополнение по поводу 3.1.
Сле...Позвольте небольшое дополнение по поводу 3.1.<br /><br />Следует подчеркнуть: Dispose не просто что-то там чистит. Вызов Dispose подразумевает завершение работы с экземпляром объекта.<br />Если, допустим, Dispose нужен только для отписки от событий (и мы об этом знаем), то возможен следующий вариант:<br />1. Создаём экземпляр объекта.<br />2. Экземпляр подписывается на какие-то события.<br />3. Используем этот объект.<br />4. Вызываем Dispose.<br />5. Объект не выбрасываем, вместо этого возвращаемся к п.2.<br /><br />Пример из жизни: в популярной библиотеке MVVM Light для этих целей существует специальный интерфейс ICleanup.<br />http://stackoverflow.com/questions/2963151/cleanup-vs-disposebool-in-mvvm-light<br /><br />Всё это, конечно, не означает, что Dispose не должен уметь корректно отписываться.TDenishttps://www.blogger.com/profile/13642350895630960387noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-67377056155049496132013-08-13T09:26:20.716+03:002013-08-13T09:26:20.716+03:00Вадим, посмотрите на акценты в этих советах.
Джеф...Вадим, посмотрите на акценты в этих советах.<br /><br />Джеффри пишет, что в подавляющем случае вызывать Dispose не нужно, но иногда можно.<br /><br />Даффи пишет, чтобы вы сделали все для того, чтобы управлять ресурсами явно, но не переживали, если вас сделать этого не удается.<br /><br />ИМХО это большая разница, поскольку опыт подсказывает, что зачастую код не расшаривает disposable объекты с другими объектами/потоками, что делает их явное освобождение предпочтительным.<br /><br />Более того, в некоторых случаях (отписка от событий, освобождение блокировок/мьютексов, закрытие файлов/сокетов) отсутствие явного освобождения ресурсов приведет к непредсказуемому поведению приложения.<br /><br />А чем мои доводы не устраивают? Я пишу о том, что Рихтер завязался на граничный случай применения disposable объектов. Вы с этим не согласны?Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-86244440066902217112013-08-13T09:07:45.180+03:002013-08-13T09:07:45.180+03:00Даффи: "Если тип реализует интерфейс IDisposa...Даффи: "Если тип реализует интерфейс IDisposable и владение ресурсами очевидно, вы должны сделать все возможное для вызова метода Dispose после завершения использования объекта"<br /><br />Рихтер: "Я рекомендую явно вызывать метод Dispose только тогда, когда вы точно знаете, что в этом месте требуется очистка ресурсов"<br /><br />Вам не кажется что они говорят об одном и том же? Уверен что нигде не используется, вызывай. Не уверен - не вызывай.<br /><br />То, что вы написали, просто показывает как можно использовать Dispose. Почему вы против слов Рихтера и за слова Даффи, и почему вы считает что ваши доводы против слов Рихтера и за доводы Даффи категорически непонятно.Anonymoushttps://www.blogger.com/profile/06588521501066186566noreply@blogger.com