Как работает интерфейс IDisposable? - PullRequest
9 голосов
/ 10 марта 2009

Я понимаю, что он используется для освобождения неуправляемых ресурсов, однако я не совсем понимаю, когда на самом деле вызывается Dispose. Я знаю, что он вызывается в конце блока using, но вызывается ли он и при сборке мусора?

Ответы [ 5 ]

11 голосов
/ 10 марта 2009

Если вы правильно внедрили IDisposable, вы должны также включить финализатор, который вызовет Dispose () для вашего объекта.

Если вы сделаете это, он будет вызван GC. Тем не менее, все равно ОЧЕНЬ хорошая идея всегда пытаться утилизировать эти объекты самостоятельно.

Самая большая проблема с использованием финализатора для вызова Dispose заключается в том, что это произойдет в другом потоке, который вы не контролируете. Это может иметь неприятные последствия в определенных ситуациях, в том числе вызывать исключение, которое происходит в потоке GC, что не очень хорошо, а также иметь выбранное поле, которое вы проверяете. Это также часть того, почему важно включить GC.SuppressFinalize (this) в ваш метод Dispose () - после удаления объекта вы не хотите его повторно размещать.

3 голосов
/ 10 марта 2009

Утилизация вызывается в нескольких местах:

  1. В конце использования блока.
  2. При явном вызове (например, в попытке {} finally {}.)

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

РЕДАКТИРОВАТЬ: Я ошибся. Утилизация НЕ вызывается во время сбора мусора. См. эту статью.

2 голосов
/ 10 марта 2009

Нет, он не вызывается при сборке мусора. Если вам нужно такое поведение, вы можете использовать деструктор (финализатор) и оттуда вызвать Dispose().

Как вы говорите, он автоматически вызывается и заканчивается using блоком.

2 голосов
/ 10 марта 2009

Dispose () вызывается в конце блока Using, чтобы вы могли рассчитывать на действия Dispose (например, закрытие соединения БД), которые будут выполняться, когда объект выходит из области видимости. Все просто.

Обновление: в вызове Dispose нет ничего специфического для неуправляемых ресурсов (хотя это обычное использование).

Обновление 2 : возникла небольшая дискуссия по теме, которую начал Рид Копси, которая полезна для понимания IDisposable. Я очень рекомендую эту статью для людей, желающих узнать больше.

В двух словах, класс IDisposable позволяет вам явно обрабатывать освобождение ресурсов (обычно неуправляемых ресурсов или подключений к базе данных) с помощью метода Dispose (). Экземпляры класса IDisposable должны создаваться в блоке «Using», чтобы обеспечить фактический вызов метода Dispose. Если вам не удастся сделать это (или вызвать его явно в блоке «finally» и т. Д.), Тогда ваш метод Dispose не будет вызываться, и вы потеряете объекты, которые хотите очистить. В всех случаях я помещаю одноразовые классы в блоки Using, и вы тоже должны.

В качестве альтернативы вы можете обрабатывать очистку ресурсов в Финализаторе (класс Destructor). Это будет вызвано автоматически, когда класс GC'd. Недостатки этого подхода состоят в том, что вы не будете явно контролировать , когда объекты очищены, и есть некоторые проблемы с многопоточностью, с которыми приходится сталкиваться. Так, например, проблемы в деструкторах очень трудно отлаживать из-за того, что они вызываются асинхронно и в другом потоке. Единственное преимущество заключается в том, что вам не нужно не забывать звонить своему деструктору, как вы это делаете. Конечно, если вы всегда используете «Использование блоков», это не проблема.

ПРИМЕЧАНИЕ. Я, ktrauberman, Reed и Pontus, сделал несколько хороших замечаний о том, как вы можете обойти те моменты, которые я делаю ниже. Это то, что я делаю, но я не могу утверждать, что это единственный способ сделать что-то. Действительно, Microsoft даже рекомендует в некоторых случаях вызывать Dispose () из финализатора. Тем не менее, я оставлю обсуждение здесь просто в качестве иллюстрации того, почему важно следовать совету Рида относительно осторожности при смешивании деструкторов и утилизации ().

Если я не согласен с ответом Рида, я утверждаю, что вы должны реализовать класс IDisposable, вызвав Dispose () в вашем Финализаторе. Это просто объединяет две разные конструкции и может привести к проблемам. Например, если вы создадите свой экземпляр класса IDisposable в блоке Using и и вызовете Dispose () в Destructor, он будет вызван дважды - с потенциально неприятными и трудными для отладки сбоями (опять же, вы не не контролирую сроки ГК). (Примечание: это действительно относится к деструкторам в целом - при определенных обстоятельствах их можно вызывать более одного раза!)

0 голосов
/ 10 марта 2009
...