Рекомендации по обращению с IDisposable
У меня есть иерархия классов, каждый из которых может создавать объекты IDisposable
.
Я добавил свойство List<IDisposable>
базовому классу в этой иерархии, к которому я добавляю любые одноразовые объекты при создании. Корневой метод Dispose
выполняет итерацию через этот список и вызывает Dispose
для каждого элемента в своем списке и очищает список. В приложении я явно вызываю метод верхнего объекта Dispose
, вызывая удаление каскада через иерархию.
Это работает, но есть ли лучший способ? Я невольно дублирую некоторые функции, уже присутствующие в рамках?
(Обратите внимание: объекты, о которых идет речь, имеют срок службы, что исключает возможность их просто вставить в блок using
или утилизировать их в том же методе, в котором они созданы.)
Edit
Просто для разъяснения - я поддерживаю только те объекты, которые нужно сохранить. Некоторые из них утилизируются в том же методе, в котором они созданы, но многие из них используются таким образом, что это невозможно.
Ответы
Ответ 1
Пока вы правильно реализуете одноразовый шаблон (как описано здесь), этот метод в порядке.
Насколько мне известно, только теги using
имеют специальную поддержку для IDisposable
- в структуре не существует ничего, что реплицирует то, что вы делаете.
Ответ 2
Нет, это правильно. IDisposable
предназначен для освобождения неуправляемых ресурсов и должен быть вызван как можно скорее после того, как вы закончите с экземпляром. Это распространенное заблуждение, что это не нужно, или что finailizer сделает это автоматически, когда объект будет собран мусором. Это не.
Правильный шаблон для IDisposable
здесь здесь и приведен ниже для быстрой справки.
public class Resource : IDisposable
{
// Dispose() calls Dispose(true)
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// NOTE: Leave out the finalizer altogether if this class doesn't
// own unmanaged resources itself, but leave the other methods
// exactly as they are.
~Resource()
{
// Finalizer calls Dispose(false)
Dispose(false);
}
// The bulk of the clean-up code is implemented in Dispose(bool)
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
}
// free native resources here if there are any
}
}
Ответ 3
Если вы говорите о произвольных объектах IDisposable
, я не думаю, что он существует.
Класс System.ComponentModel.Container
реализует каскадный Dispose, но требует выполнения элементов IComponent
. Если вы управляете своими объектами IDisposable
, вы можете заставить их реализовать IComponent
- для этого требуется только одно свойство Site
, которое может возвращать null
.
Ответ 4
Похоже на ситуацию, когда шаблон visitor может быть уместным. Хотя я никогда не понимаю утверждения о том, что он расширяет ваши классы и оставляет их неизменными, потому что я знаю только примеры, когда классы должны иметь метод AcceptVisitor
или тому подобное. Кстати, это не шаблон, который мне нравится, потому что он сложный и имеет тенденцию загромождать код.
Ответ 5
Если один объект создаст много других объектов IDisposable и сохранит их право собственности на протяжении всего своего существования, шаблон, который вы описываете, может быть хорошим. Это может быть усилено тем, что ваш метод реализации класса "T RegDispos <T> (T вещь), где T: IDisposable;" который добавит одноразовое к списку и вернет его. Таким образом, можно позаботиться о создании и очистке ресурса в одном и том же выражении, заменив оператор вроде someField = someDisposType.CreateThing(); "с" someField = RegDispos (someDisposType.CreateThing());".
Если ваш класс не предоставляет публичный конструктор (требуется, чтобы посторонние использовали методы factory), и если вы используете vb.net или хотите использовать потоки-статические поля, вы можете даже комбинировать инициализацию и очистку с объявлением (например, var someField = RegDispos (someDisposType.CreateThing()); "). Чтобы это было безопасно, конструктор должен быть вызван в блок try/catch или try/finally, который может вызвать Dispose на созданных под-объектах, если сбой конструкции. Поскольку инициализаторы полей в С# не имеют доступа к параметрам конструктора (слабость языка, IMHO), единственный способ реализовать такой шаблон состоит в том, чтобы создать способ factory создать список и поместить его в поточно-статическую переменную который затем может быть прочитан статическим методом RegDispos.