Дилемма дизайна: кто должен обрабатывать одноразовый параметр? - PullRequest
8 голосов
/ 24 июня 2011

Если мой класс использует доступный ресурс в своем конструкторе (DbConnection, если это имеет значение), следует ли мне реализовать IDisposable в моем классе и удалить объект DbConnection, или позволить пользователю обрабатывать удаление DbConnection?

В настоящее время я реализую IDisposable в своем классе, но теперь я вижу некоторые возможные негативные эффекты: дизайн класса беспорядка, двойное удаление DbConnection при неправильном использовании. Но есть и положительные: основным является простое использование (особенно если вы используете несколько одноразовых параметров).

В «диком» виде я вижу оба подхода, поэтому не могу определиться ..

Обновление: Спасибо всем за ответы, которые на самом деле показали, что это действительно не простой выбор. И трудно выбрать правильный ответ тоже. Однако я решил придерживаться самого простого в будущем. Итак, окончательный выбор: не реализовывать IDisposable.

Ответы [ 4 ]

4 голосов
/ 24 июня 2011

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

4 голосов
/ 24 июня 2011

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

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

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

1 голос
/ 24 июня 2011

Я бы либо :

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

или:

  1. Реализация IDisposable;вызывающий код отказывается от владения переданным объектом и должен обращаться с ним как с уже удаленным.

И четко документируют, какой выбор я сделал.ИМХО слишком вероятно, что попытка владения одноразовым объектом, находящимся в нескольких местах, однажды пойдет не так.

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

0 голосов
/ 25 июня 2011

В некоторых ситуациях объект будет использовать одноразовый ресурс, срок полезного использования которого превышает срок службы нового объекта, который его использовал.Будут другие ситуации, когда можно будет передать объект объекту, который ничего не знает ни о каких одноразовых элементах внутри него.Некоторые типы объектов могут оказаться использованными в обоих сценариях.

Рассмотрим гипотетический тип IDisposable SoundPlayer, который будет воспроизводить объект IDisposable SoundSource, а затем утилизировать себя, когда это будет сделано.Легко представить себе ситуации, когда хочется иметь возможность загрузить SoundSource один раз, воспроизвести его несколько раз, а затем вручную утилизировать.Также легко представить себе ситуации, когда нужно загрузить объект SoundSource, а затем «запустить и забыть» проигрыватель.Создатель SoundSource не может утилизировать его до тех пор, пока игрок не закончит, но впоследствии он будет бесполезен, поэтому наиболее удобный способ действий для SoundPlayer - избавиться от него.

Я бы предположил, чтоесли оба сценария по меньшей мере правдоподобны, вы должны либо предоставить фабричный метод, который передает владение IDisposable вместе с тем, который этого не делает, либо у вас должен быть фабричный метод с параметром, который указывает, следует ли передать владение.Обратите внимание, что передача IDisposables в конструкторы для классов, которые должны их использовать и утилизировать, опасна, поскольку Microsoft не позволяет блокировать блоки try / catch вокруг конструкторов, вызываемых из инициализаторов полей, и трудно гарантировать, что что-то будет удалено при выдаче конструктора.При вызове фабричного метода конструктор значительно упрощает перехват ошибок (хотя это по-прежнему сложно - особенно в C #).

Edit - Addendum Другой подход, который иногда полезен, заключается впусть объект либо опубликует событие Disposed, либо примет делегат в своем конструкторе для запуска, когда переданный объект больше не нужен.Я предпочитаю явный делегат на событие, так как использование события подразумевает, что подписчик события (человек, создающий объект, который временно будет хранить одноразовый ресурс) обязан отказаться от подписки, что добавляет дополнительные сложности, особенно если объект может бытьпередал еще один объект.Поскольку наиболее распространенная вещь, которую вызывающий объект захочет сделать, когда получатель объекта больше не нуждается в том, чтобы объект был просто удален, простейший случай - просто иметь возможность для получателя объекта обрабатывать удаление самостоятельно..

...