Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Попробуй соорудить реализацию объекта с Dispose в которой бы: КД>- было запрещено использовать объект после вызова Dispose. КД>- Dispose не "разрушал" объект, пока работают другие его методы.
Нужен, как я понял, shared ресурс: счетчик ссылок в руки, он как раз для этого придуман. Файлы и сокеты не грозят циклическими ссылками (кроме каких-нибудь патологических случаев), так что механизм отлично будет работать.
Кстати, Microsoft мог встроить автоматическую поддержку этого для IDisposable в компилятор/рантайм, раз уж они начали. Видимо, есть занятия поважней.
Здравствуйте, noone, Вы писали:
КД>>Попробуй соорудить реализацию объекта с Dispose в которой бы: КД>>- было запрещено использовать объект после вызова Dispose. КД>>- Dispose не "разрушал" объект, пока работают другие его методы.
N>Нужен, как я понял, shared ресурс: счетчик ссылок в руки, он как раз для этого придуман. Файлы и сокеты не грозят циклическими ссылками (кроме каких-нибудь патологических случаев), так что механизм отлично будет работать. N>Кстати, Microsoft мог встроить автоматическую поддержку этого для IDisposable в компилятор/рантайм, раз уж они начали. Видимо, есть занятия поважней.
Не, это только начало.
Допустим, правильную реализацию Dispose мы победили.
А теперь следующая задача.
Есть два связанных объекта. Пусть это будет подключение и транзакция. И нужно запретить диспозить подключение, пока работает код в объекте транзакции.
Опять юзаем счетчик ссылок. Только счетчик находится в одном объекте, а инкрементит/декрементит его другой объект.
И еще нужно не забыть прикрутить ко всему этому делу CER, чтобы гарантированно выполнить декремент. Я прав?
Вообщем, лично у меня сформировалось мнение — что счетчик ссылок это костыли не для плюсов (где, формально, это фундамент для более менее сложных вещей, и его не замечают так же как воздух), а для C#-а
---
Но мне нравится писать на обоих языках
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, MTD, Вы писали:
MTD>Здравствуйте, Евгений Акиньшин, Вы писали:
MTD>>> но замечу, что циклические ссылки — проблема кривого дизайна
ЕА>>Это когда они стали плохим дизайном и почему
MTD>Уточнение — владеющие. Это из контекста понятно.
Ну может кому-то и понятно, а в управляемых средах нет необходимости в искусственных разграничениях на владеющие\ не владеющие ссылки
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Здравствуйте, Sinix, Вы писали:
КД>>>Попробуй соорудить реализацию объекта с Dispose в которой бы: КД>>>- было запрещено использовать объект после вызова Dispose. КД>>>- Dispose не "разрушал" объект, пока работают другие его методы.
S>>Дотнетовский Dispose заточен под самый популярный сценарий — один владелец, объект гарантированно не будет использоваться в дальнейшем.
КД>Кем гарантируется? Программистом?!
Языком (см using). При нормальном процессе разработки ещё и FxCop-ом на билдсервере
dispose + using заточен под сценарии, когда время жизни ресурса заведомо меньше цикла сборки мусора, иначе освобождение ресурсов (если оно вообще требуется) спокойно может переехать в финалайзер.
как наглядный пример:
using (var fileWriter = OpenFile(...))
{
// запись в файл
// ...
} // Происходит вызов fileWriter.Dispose()
// здесь к fileWriter-у при всём желании не обратишься.
Если настроен FxCop и забыли обернуть xmlWriter в using — будет предупреждение (или ошибка, смотря как настроить) при сборке.
Если ваш ресурс должен жить столько же, сколько овнер, то овнер тоже должен реализовать IDisposable и освобождать используемые ресурсы. Забыли — снова варнинг.
КД>Упаси меня Господь закладываться на такие гарантии
Эмм... какие ещё гарантии нужны?
>> Я бы смотрел в сторону RX/тасков. КД>Не, спасибо (хоть бы ссылку дали, что-ли, для темных).
Но перед тем, как туда лезть, придётся основательно подучить матчасть и определиться с задачей, иначе выйдет только хуже.
Если проблема вот в этом:
Есть два связанных объекта. Пусть это будет подключение и транзакция. И нужно запретить диспозить подключение, пока работает код в объекте транзакции.
то использование dispose напрямую вам ничем не поможет. Если отвлечься от самой проблемы:
— транзакция подписывается на connection.Closing и кидает исключение, если что не так.
— соединение хранит внутри себя аналог RefCountDisposable из Rx и отдаёт транзакции ссылку через GetDisposable()
Если не отвлекаться — нужно подробно изучать вот этот док. Ресурсы в транзакции (особенно в распределённых транзакциях) — достаточно сложная штука, свои велосипеды вылезут боком.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Здравствуйте, noone, Вы писали:
КД>Есть два связанных объекта. Пусть это будет подключение и транзакция. И нужно запретить диспозить подключение, пока работает код в объекте транзакции. КД>Опять юзаем счетчик ссылок. Только счетчик находится в одном объекте, а инкрементит/декрементит его другой объект. КД>И еще нужно не забыть прикрутить ко всему этому делу CER, чтобы гарантированно выполнить декремент. Я прав?
Аббревиатуру не осилил, но идея понятна. Да, все как ты сказал.
КД>Вообщем, лично у меня сформировалось мнение — что счетчик ссылок это костыли не для плюсов (где, формально, это фундамент для более менее сложных вещей, и его не замечают так же как воздух), а для C#-а
Сам механизм подсчета ссылок не хороший и не плохой, он просто есть и имеет некоторую семантику. В C++ он реализован через костыль — семейство *_ptr и используется везде, в C# — тоже (классы с ручным retain/release) и используется только для внешних к программе ресурсов. Пример подсчета без костылей — современный Objective-C, где сохраняются все достоинства подсчета и его недостатки, но, по крайней мере, он поддерживается непосредственно компилятором. Это очень важное свойство — доступна куча статических проверок и взаимодействие между библиотеками от разных автором в области управления памятью прозрачное насколько это возможно.
КД>--- КД>Но мне нравится писать на обоих языках
Здравствуйте, noone, Вы писали:
КД>>Есть два связанных объекта. Пусть это будет подключение и транзакция. И нужно запретить диспозить подключение, пока работает код в объекте транзакции. КД>>Опять юзаем счетчик ссылок. Только счетчик находится в одном объекте, а инкрементит/декрементит его другой объект. КД>>И еще нужно не забыть прикрутить ко всему этому делу CER, чтобы гарантированно выполнить декремент. Я прав?
N>Аббревиатуру не осилил, но идея понятна. Да, все как ты сказал.
Это я про "Constrained Execution Regions" говорил.
N>Сам механизм подсчета ссылок не хороший и не плохой, он просто есть и имеет некоторую семантику. В C++ он реализован через костыль — семейство *_ptr
Я всегда думал, что "костыль" (в плане счетчика ссылок) это когда руками вызываешь AddRef/Release и постоянно паришься чтобы не облажаться с ними. В нормально спроектированной программе таких проблем нет.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, MTD, Вы писали:
MTD>Здравствуйте, noone, Вы писали:
N>>Этот бред имеет отдельную поддержку от языка в C# — http://msdn.microsoft.com/en-us/library/ms173171(v=vs.80).aspx . Наверное, от скуки они его туда 10 лет назад приделали, тебя потроллить.
MTD>Нет, тролишь здесь похоже ты. Или ты приведешь адекватный пример?
Это натурально простейший практический кусочек, проще него только репа. Примеров на C# можно обчитаться в вышеприведенной ссылке. Вот микропример на Java
public class MyPanel extends JPanel {
JButton myButton;
private void initButton() {
myButton = new JButton("Do something");
myButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doSomething();
}
});
add(myButton);
}
private void doSomething() {
}
}
При подсчете ссылок придется явно городить слабую ссылку на this (костыль). В чуть более сложном случае — курить бамбук (много костылей) с риском получить висячие ссылки/утечки.
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Здравствуйте, noone, Вы писали:
КД>Это я про "Constrained Execution Regions" говорил.
Это вовсе необязательно. Декремент делается в finally.
N>>Сам механизм подсчета ссылок не хороший и не плохой, он просто есть и имеет некоторую семантику. В C++ он реализован через костыль — семейство *_ptr
КД>Я всегда думал, что "костыль" (в плане счетчика ссылок) это когда руками вызываешь AddRef/Release и постоянно паришься чтобы не облажаться с ними.
Да. А еще костыль это еще когда тулзы не могут при взгляде на программу сказать, что же тут происходит. Или когда в дебаггере черт ногу сломит из-за лишних объектов. Когда умных указателей целый зоопарк, и в каждой библиотеке он свой, и есть тонна имплицитных преобразований между одними и другими (пользуясь случаем, передаю привет WebKit вообще и PassRefPtr в частности). Одним словом, костыль это когда семантика языка плохо ложится на задачу и средствами языка правится плохо.
Здравствуйте, noone, Вы писали:
КД>>Это я про "Constrained Execution Regions" говорил.
N>Это вовсе необязательно. Декремент делается в finally.
Ага. Он у меня и делается в finally. Надо только что бы этот finally гарантировано вызвался. Для этого и юзаю CER. Может я что конечно попутал в этом деле, но решил перестраховаться по-полной
Впрочем, я сверялся с кодом из самого фреймворка, и думаю я ничего не напутал.
N>Да. А еще костыль это еще когда тулзы не могут при взгляде на программу сказать, что же тут происходит. Или когда в дебаггере черт ногу сломит из-за лишних объектов. Когда умных указателей целый зоопарк, и в каждой библиотеке он свой, и есть тонна имплицитных преобразований между одними и другими (пользуясь случаем, передаю привет WebKit вообще и PassRefPtr в частности). Одним словом, костыль это когда семантика языка плохо ложится на задачу и средствами языка правится плохо.
Не, ну зоопарк можно где угодно соорудить
У меня только два типа смарт-указателей — для COM-а и для моих классов. И они никак не пересекаются. Можно считать — полная гармония и умиротворенность
А от всяких ужосов типа буста и еже с ними — я (по правде говоря) шарахаюсь
Со сложностью борюсь всякими злобными методами. Перечислять которые нет смысла.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Kubyshev Andrey, Вы писали:
KA>Лет 10 ездил на коробке. Думал нафига мне автомат, коробка это же "все под контролем". Пересел на автомат, на коробку обратно не хочу
По годным дорогам без снега и пробок — разница минимальна между ними.
KA>СиПлюсПлюсил много лет, попробовал по-СиШарпить — обратно не хочется.
Странно, переходил пару раз в обе стороны, особых неудобств не замечал.
Как вам верно заметили выше, при наличии некоторого опыта за плечами и умением пользоваться STL или лучше ATL, управление памятью в С++ практически перестаёт создавать проблем.
Алгоритмы с графами реализуются элементарно, как только поймёте шо это не ноды владеют друг другом, а граф владеет всеми нодами, которые конечно ссылаются друг на друга, но не владеют.
В качестве бесплатного бонуса, вы получите ускорение алгоритма на десятки процентов, потому шо становится технически просто разместить все ноды графа в последовательных адресах памяти.
Из языков на которых программирую, сложнее всего с управлением памятью в Оbjective-C — у Apple как обычно всё через задницу.
Здравствуйте, Евгений Акиньшин, Вы писали:
ЕА>Ну может кому-то и понятно, а в управляемых средах нет необходимости в искусственных разграничениях на владеющие\ не владеющие ссылки
Нет необходимости, только если класс не содержит неуправляемых ресурсов. В протипвном случае, вылезают всякие неочевидные вещи, типа
Смешались в кучу кони, люди ... Встроенная в платформу реализация publisher/subscriber это event. Delegate это просто функциональный тип. И да, можно обходится и без event, как в других языках, Rx тому примером.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Встроенная в платформу реализация publisher/subscriber это event.
Да, это один из самых популярных методов, которые мне попадались, "как сделать утечку памяти на C#".
Здравствуйте, noone, Вы писали:
N>Это натурально простейший практический кусочек, проще него только репа. Примеров на C# можно обчитаться в вышеприведенной ссылке. Вот микропример на Java
N>При подсчете ссылок придется явно городить слабую ссылку на this (костыль). В чуть более сложном случае — курить бамбук (много костылей) с риском получить висячие ссылки/утечки.
Вот, наконец адекватный пример. Так вот, если из плюсов пытаться яву сделать, то да, будет связка shared_ptr/weak_ptr. А если на плюсах писать, то будет так:
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Смешались в кучу кони, люди ... Встроенная в платформу реализация publisher/subscriber это event. Delegate это просто функциональный тип. И да, можно обходится и без event, как в других языках, Rx тому примером.
OK. Назовем все это Observer — лучше?
То, что можно обойтись без чего угодно, я знаю. Вернемся к исходному вопросу: могу ли я сохранить объект, который мне пришел в метод/функцию, или нет? И что делать, когда объект точно нужен, но сохранять его нельзя, т.к. есть вероятность цикла? Правильно: перепиливаем архитектуру, добавляем хаки по вкусу. Или используем GC.
Здравствуйте, MTD, Вы писали:
MTD>Вот, наконец адекватный пример. Так вот, если из плюсов пытаться яву сделать, то да, будет связка shared_ptr/weak_ptr. А если на плюсах писать, то будет так:
Это тот же самый пример (чуть проще). Интересно, что ты его узнал только когда речь пошла о UI.
MTD>
Здесь мы наблюдаем, как байндится raw-указатель this, то есть явно используется факт того, что кнопка живет не дольше this. Чуть усложним Java-пример:
interface Listener {
void doSomething();
}
public class MyPanel extends JPanel implements Listener {
JButton myButton;
public void initButton(final Listener listener) {
myButton = new JButton("Do something");
myButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listener.doSomething();
}
});
add(myButton);
}
@Override
public void doSomething() {
}
public static void main(String[] args) {
MyPanel p = new MyPanel();
p.initButton(p);
p.initButton(new Listener() {
@Override
public void doSomething() {
}
});
}
}
Теперь в initButton может передаваться как this, так и совершенно посторонний listener. Время жизни зарегистрированного listener должно быть не меньше времени жизни кнопки.
Здравствуйте, Константин, Вы писали:
К>Из языков на которых программирую, сложнее всего с управлением памятью в Оbjective-C — у Apple как обычно всё через задницу.
Как оно может быть сложнее С++, если там shared_ptr и weak_ptr встроены в язык?
Здравствуйте, noone, Вы писали:
N>Как оно может быть сложнее С++, если там shared_ptr и weak_ptr встроены в язык?
C++
Опасные вещи, такие как операторы new/delete нужны крайне редко.
Если в полях объекта только smart pointers и generic collections, то при удалении объекта все его поля удалятся автомагически.
Если конструировать top-level objects только на стеках – вам вообще не нужно заниматься управлением памятью, за вас всё сделают язык, компилятор и библиотеки.
Obj-C
Даже при использовании рекомендуемой практики @property(retain), если вы забыли освободить какое-то поле в методе -(void)dealloc, вот она утечка.
Случайно сослались на self в блоке — получили циклическую ссылку.
Более безопасных альтернатив не существует.
Здравствуйте, noone, Вы писали:
N>То, что можно обойтись без чего угодно, я знаю. Вернемся к исходному вопросу: могу ли я сохранить объект, который мне пришел в метод/функцию, или нет? И что делать, когда объект точно нужен, но сохранять его нельзя, т.к. есть вероятность цикла? Правильно: перепиливаем архитектуру, добавляем хаки по вкусу. Или используем GC.
Тут суть не столько в Publisher/Subscriber, сколько в замыканиях. И таки да, известный факт — без GC замыкания неюзабельны.