Проблемный пример шаблона IDisposable? - PullRequest
6 голосов
/ 14 июля 2010

Допустим, у вас есть 3 класса, которые реализуют IDisposable - A, B и C. Оба класса A и B зависят от класса C.

  1. Было бы правильно сказать, что классы Aи типичная реализация B Dispose () будет:

    public void Dispose()
    {
        if (m_C != null) m_C.Dispose();
    }
    
  2. Если есть экземпляр A и и экземпляр B, которые совместно используют один и тот же экземпляр CКак бы вы преодолели проблему, заключающуюся в том, что удаление экземпляра A могло бы повредить экземпляр B?

  3. Приложение в последнюю минуту - если в пункте 2 это контейнер DI, который создает все экземпляры, ктоотвечает за утилизацию предметов?Это сам контейнер?Как?

Спасибо, Уриг

Ответы [ 10 ]

12 голосов
/ 14 июля 2010

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

Если A и B должны ссылаться на один и тот же экземпляр C, то только один из них должен действовать как «владелец».

Хотя вы можете делать то, что равносильно подсчету ссылок, я обычно считаю, что лучше просто задокументировать, кому что «принадлежит». Например, когда вы создаете Bitmap с потоком, с этого момента Bitmap владеет потоком, и вы не должны распоряжаться им самостоятельно. Это может вызвать несколько проблем, но в конечном итоге это проще, чем пытаться ускорить подсчет ссылок.

4 голосов
/ 14 июля 2010

Выполнение нулевой проверки не поможет, как будто B избавляется от C, это не обновит ссылку A.

Вы должны убедиться, что только один из классов владеет C. Этот класс-владелец отвечает за его удаление.

Обычно класс, который создает C, должен быть классом, который им располагает.

1 голос
/ 15 июля 2010

Приложение в последнюю минуту - Если в пункте 2 это DI-контейнер, который создает все случаи, кто несет ответственность за избавление от объектов? Это сам контейнер? Как?

Да, контейнеру принадлежат любые IDisposable объекты, которые он создает. Контейнер утилизирует эти объекты, когда он утилизируется сам. Все контейнеры DI уже должны делать это по умолчанию.

Иногда структура DI дает вам возможность стать владельцем. Например, в Autofac вы можете попросить ввести Owned<T>, а затем вы можете безопасно позвонить Owned<T>.Dispose() самостоятельно, когда закончите с объектом. Это особенно полезно, если вы динамически создаете экземпляры с помощью встроенной фабрики Func<Owned<T>>. Обратите внимание, что такие «собственные экземпляры» не предназначены для совместного использования.

1 голос
/ 14 июля 2010

Кто создал экземпляр? Обычно это владелец и он должен нести ответственность за удаление экземпляра.

Скорее всего, у вас есть "внешний" класс, который создал C и затем передал его, прямо или косвенно, в A и B. Это, вероятно, естественный кандидат, который несет ответственность за жизненный цикл C и должен избавиться от него .

[Редактировать: в ответ на комментарий ОП] Звучит так, что, может быть, вам стоит еще раз взглянуть на дизайн здесь. Указывает ли это на необходимость проведения рефакторинга?

У вас есть класс C, который нуждается в утилизации, который используется как A, так и B; должен ли у вас быть класс, который несет полную ответственность за сортировку C через A и B вместо того, чтобы они сами создавали C из контейнера DI? Или C действительно больше синглтона. Разве это действительно нужно утилизировать?

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

1 голос
/ 14 июля 2010

Только один экземпляр должен быть владельцем, и он отвечает за утилизацию.Экземпляр, не являющийся владельцем, должен получить ссылку на C с помощью функции, такой как Attach, и не должен ее утилизировать.

0 голосов
/ 30 мая 2014

Каждый IDisposable объект должен иметь одного владельца. Если ресурс нужно будет разделить между несколькими пользователями, любой из которых может использовать его последним, тогда каждый пользователь должен хранить ссылку на свою собственную оболочку. Объекты-обертки должны затем использовать какие-то средства, отличные от IDisposable, для координации с одним частным объектом внутренней обертки, который затем вызовет Dispose для ресурса. Внутреннему объекту-обертке не нужно использовать IDisposable для очистки, потому что он не доступен публично, а тот факт, что ему не нужно использовать IDisposable, означает, что он может использовать средство очистки, которое поддерживает несколько владельцев. 1006 *

0 голосов
/ 16 июля 2010

В дополнение к тому, что сказал Джон - создатель является владельцем и должен распоряжаться одноразовым.

В данном случае это контейнер, а контейнер отвечает за утилизацию компонентов. Не каждый компонент поддерживает это (или, по крайней мере, не каждый полностью). Замок Виндзор делает . Также Autofac поддерживает это.

0 голосов
/ 14 июля 2010

Это была бы правильная реализация: однако, вы можете сохранить ссылки на все объекты в зависимости от конкретного экземпляра C в A и B, и иметь проверку на пустой список (за исключением текущего объекта утилизации) в методе C Dispose.

0 голосов
/ 14 июля 2010
  1. Обычно я делаю это так, как правило, принимаются, и это определенно работает. Однако, если другой объект уничтожил его, проверка на нуль не остановит повторный вызов, поскольку он не будет нулевым. Однако распоряжение С должно защищать от нескольких вызовов.

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

Я бы также спросил, где вы могли бы увидеть это на примере из реальной жизни, возможно, это несоответствие дизайна. Обновление: , как уже упоминали другие, все сводится к проблеме дизайна - вы получаете аналогичный эффект при использовании CCW, кто-то еще выпускает базовый COM-объект, где другие пользователи все еще могут его использовать.

0 голосов
/ 14 июля 2010

Я мог бы подумать, что есть два метода:

  1. Создать родительскую коллекцию в C, а в методе dispose A и B удалить self из родительской коллекции дочернего элемента.Затем, если количество родительских коллекций равно 0, вызовите dispose.
  2. Ленивая загрузка свойства в пределах A и B для доступа к C. Выполните нулевую проверку C, если какой-либо другой объект уничтожил его, повторно создайте его (есливозможный).
...