Проблематичный пример шаблона IDisposable?

Скажите, что у Вас есть 3 класса, которые реализуют IDisposable - A, B и C. Классы A и B оба зависят от класса C.

  1. Был бы это быть корректным, чтобы сказать, что классы A и типичная реализация B Располагают (), был бы:

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

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

Спасибо, urig

6
задан urig 14 July 2010 в 11:11
поделиться

9 ответов

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

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

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

12
ответ дан 8 December 2019 в 12:18
поделиться

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

0
ответ дан 8 December 2019 в 12:18
поделиться

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

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

[Edit: in reply to OP's comment] Похоже, что вам стоит еще раз взглянуть на дизайн. Указывает ли это на необходимость рефактора?

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

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

1
ответ дан 8 December 2019 в 12:18
поделиться

Два метода, о которых я мог бы подумать:

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

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

1
ответ дан 8 December 2019 в 12:18
поделиться
  1. Обычно я делаю это таким образом, обычно меня принимают, и это определенно работает. Однако, если его удалил другой объект, проверка на null не остановит повторный вызов dispose, потому что он не будет нулевым. Однако утилизация C должна защищать от множественных вызовов.

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

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

0
ответ дан 8 December 2019 в 12:18
поделиться

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

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

Как правило, класс, создающий C, должен быть и классом, который его утилизирует.

4
ответ дан 8 December 2019 в 12:18
поделиться

Дополнение в последнюю минуту - Если в пункте 2 это контейнер DI, который создает все инстанции, кто несет ответственность за утилизация предметов? Это сам контейнер? Как?

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

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

1
ответ дан 8 December 2019 в 12:18
поделиться

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

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

0
ответ дан 8 December 2019 в 12:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: