Паттерн IDisposable

Я не зря запостил сразу весь исходник класса BufferPool. В дальнейшем я собираюсь пройтись по тем маленьким решениям, которые неизбежно приходится принимать при проектировании и реализации классов и объяснить, почему я принял те решения, которые принял. Итак, паттерн IDisposable. Все знают что в .NET нет детерминированных деструкторов. Разработчики сделали все чтобы скрыть от нас тот момент, когда класс освобождается. Мы не можем вызвать деструктор сами. Взамен предлагают два подхода:
  1. IDisposable.Dispose() или Close()
  2. Функция Finalize() (имеет синтаксис деструктора в C#)
Когда и какой надо использовать?

IDisposable реализуем если выполняется одно или оба условия:

  1. Данный класс содержит неуправляемые (unmanaged) ресурсы.
  2. Данный класс содержит члены, которые реализуют IDisposable

Finalize реализуем только в случае неуправляемых ресурсов. Внутренние классы освобождать вызовом Dispose() из Finalize() ненадо, даже запрещено(!), так как они уже могут быть освобожденны с помощью финализации. Собственно сам паттерн IDisposable выглядит так:

class Foo : IDisposable {
bool disposed = false;

public Foo() {
}

~Foo() {
Dispose(false);
}

public void Dispose() {
Dispose(true);
}

// Метод Dispose должен быть написан так, чтобы допускать
// много вызовов (игнорировать повторные)
protected virtual void Dispose(bool disposing) {
// сделать общие действия для Dispose и для Finalize
if (disposing) {
// сделать то что должен делать Dispose
// .....
disposed = true;
// сказать GC, что Finalize для данного класса
// вызывать ненадо
GC.SuppressFinalize(this);

} else {
// сделать то, что должен делать Finalize.
// ВАЖНО: нельзя обращаться к элементам
// нашего класс со ссылочными типами
// (которые не наследуются от ValueType)
// .....
}
}

// При вызове метода, которые не может быть выполнен
// без уже освобожденных ресурсов следует бросать ObjectDisposedException
public void DoWork() {
if (disposed) throw new ObjectDisposedException("Foo");
}

}
Теперь посмотрим внимательно на класс BufferPool. Что мы видим? Роль логической переменной disposed играет роль поле mrEvent, если оно == null, то значит кто-то уже вызвал Dispose(). Хорошо. Так же мы видим 2 метода Dispose(), полностью соответствующих паттерну IDisposable. Но где метод Finalize (деструктор в С#)? Правильно, его нет. Потому что наш класс непосредственно неуправляемыми ресурсами не владеет. У нас есть только mrEvent, который сам владеет unmanaged ресурсами, но освобождать их его забота, а не наша!

Коментарі

Популярні дописи з цього блогу

OAuth-аутентификация через ВКонтакте

Українська мова