Должен ли IDisposable применяться каскадно? - PullRequest
13 голосов
/ 22 сентября 2009

Это довольно простой вопрос, однако я все еще немного борюсь с ним.

IDisposable реализуется, когда вы хотите разрешить пользователю объекта освобождать базовые ресурсы (например, сокеты и т. Д.) До того, как объект будет в конечном итоге собран мусором.

Когда у меня есть класс, который содержит DbConnection (реализует IDisposable), должен ли мой класс также реализовывать IDisposable и связывать вызов до DbConnection или любых других объектов IDisposable, которыми он владеет? В противном случае ресурсы DbConnections будут освобождены только тогда, когда мой класс будет GarbageCollected, тем самым отбрасывая его ссылку на соединение, и GC завершит DbConnection.

Ответы [ 9 ]

9 голосов
/ 22 сентября 2009

Да, вы ВСЕГДА внедряете IDisposable, если вы контролируете одноразовые предметы. ВСЕГДА . Ваш код не сломается, если вы этого не сделаете, но если вы этого не сделаете, он лишает вас цели иметь одноразовые предметы.

Общее правило оптимизации GC: * ​​1007 *

  • Любой класс, который управляет объектами, не управляемыми GC, должен реализовывать финализатор (и, как правило, также должен реализовывать IDisposable). Вот откуда обычно берутся одноразовые классы «верхнего уровня» - они обычно управляют HANDLE окном, сокетом, мьютексом или чем-то еще.
  • Любой класс, который создает экземпляр IDisposable, должен сам реализовать IDisposable и правильно утилизировать () его составляющих.
  • Любая функция, которая создает экземпляр объекта IDisposeable, должна должным образом утилизировать его после завершения использования. Не позволяйте этому просто выпасть из области видимости.

Эти правила могут быть согнуты или игнорироваться, если вы пишете приложение для себя, но при распространении кода для других вы должны быть профессионалом и соблюдать правила.

Логика здесь заключается в том, что когда вы управляете памятью за пределами обзора ГХ, механизм ГХ не может должным образом управлять использованием вашей памяти. Например, в вашей куче .NET у вас может быть только 4-байтовый указатель, но в неуправляемой стране вы можете указать 200 МБ памяти. Движок GC не будет пытаться собрать их, пока у вас не будет нескольких десятков, потому что все, что он видит, - это несколько байтов; в то время как в реальном мире это выглядит как утечка памяти.

Следовательно, правило таково, что неуправляемая память должна освобождаться сразу же после того, как вы ее используете (цепочка IDisposable делает это за вас), тогда как управляемая память освобождается механизмом GC всякий раз, когда она обходит его. *

6 голосов
/ 22 сентября 2009

Да, ваш класс должен быть IDisposable, если ему нужно избавиться от любых объектов, которые он использует. Примером этого является StreamReader. Он реализует IDisposable, поэтому может распоряжаться связанным с ним объектом потока.

3 голосов
/ 22 сентября 2009

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

Однако шаблон, используемый для Dispose (), может немного отличаться от того, что обычно пишется, в этом случае, поскольку вам не нужно различать неуправляемые и управляемые ресурсы (ваш инкапсулированный ресурс всегда обрабатывается как «управляемый»). ресурс).

Я написал подробное сообщение в блоге на эту конкретную тему - Инкапсуляция IDisposable ресурсов .

3 голосов
/ 22 сентября 2009

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

Если вы используете соединение с базой данных в качестве локальной переменной в методе, вы можете использовать оператор using () {}.

using (SqlConnection sqlConnection = new SqlConnection(connStr))<br> {<br> ...do stuff with connection here<br> }

Оператор using () {} автоматически вызывает Dispose () для объектов, объявленных в (). (Также требуется, чтобы объекты, объявленные в (), реализовали IDisposable, чтобы гарантировать, что они могут быть удалены)

Если вы вместо этого работаете с DbConnection как частной переменной, которая инициализируется во время создания объекта или каким-либо другим методом инициализации, то вы, вероятно, захотите реализовать IDisposable самостоятельно, а затем вызвать _dbConnection.Dispose () в своем методе Dispose (). Таким образом, когда ваш объект будет удален, объект соединения с БД также будет располагаться.

public class MyDALObj : IDisposable<br> {

public MyDalObj()<br> {<br> ... create _dbConn object ...<br> }

public void Dispose()<br> {<br> _dbConn.Dispose();<br> }

private DbConnection _dbConn;<br> }

2 голосов
/ 22 сентября 2009

Существует два различных сценария:

  1. Ваш объект задан ссылкой на объект для использования либо через аргумент конструктора, либо через свойство, и этот объект реализует IDisposable.
  2. Ваш объект создает экземпляр объекта, который реализует IDisposable.

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

Ваше DbConnection подпадает под этот второй случай, так что да, ваш объект должен реализовать IDisposable, а затем избавиться от соединения.

В первом случае вам нужно выбрать следующие три решения:

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

Это было много текста, поэтому позвольте мне подвести итог:

  1. Объекты, которыми вы владеете, вам необходимо утилизировать.
  2. Объекты, которых у вас нет, вы не утилизируете.

Есть и третий случай, который звучит не так, как у вас, но тем не менее.

В случае, когда вы создаете, используете и отбрасываете объект локально, внутри одного метода, не передавая его и не сохраняя его в полях класса, вместо этого вы используете оператор using, например:

using (IDbConnection conn = ....())
{
}
0 голосов
/ 22 сентября 2009

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

Я думаю, что лучший способ реализовать это - использовать комбинацию методов Dispose и Finalize. Вы можете найти больше Здесь .

0 голосов
/ 22 сентября 2009

Конечно, вы можете избежать значительных (пере) затрат на реализацию IDisposable и получить что-то очень близкое к детерминированной финализации объектов в управляемой куче, если вы используете C ++ / CLI. Это часто (я считаю) упускаемый из вида аспект языка, который многие люди, похоже, отправляют в мусорное ведро «только для клеевого кода».

0 голосов
/ 22 сентября 2009

Поскольку мы никогда не знаем, когда объект будет собираться GC, мы используем интерфейс IDisposable, чтобы иметь возможность преднамеренно высвободить неуправляемые ресурсы до того, как объект будет собран мусором. Если одноразовый объект не утилизируется до сбора, его ресурсы могут быть не освобождены до тех пор, пока не завершится работа домена приложения. Это почти неписанное правило, согласно которому каждый объект, имеющий ссылку на объект IDisposable, сам должен быть IDisposable и вызывать метод Dispose своих ссылок IDisposable в своем собственном методе Dispose.

0 голосов
/ 22 сентября 2009

Это, безусловно, лучшая практика, особенно при работе с тяжелыми / неуправляемыми объектами.

Редактировать: Лучшая практика, но не обязательно.

...