Реализация интерфейса IDisposable на c#
Когда-то давно я разобрался в и так несложном интерфейсе IDisposable и сделал "правильный" пример реализации, по сути, просто откомментировав код с MSDN, для лучшего понимания, зачем и почему нужно делать именно так:
class DisposableExample: IDisposable { #region Абсолютный минимум кода, который должен быть написан в правильной реализации IDisposable ////// Поле, где будет true, если объект уже пытались уничтожить. /// private bool isDisposed = false; ////// Этот метод реализует интерфейс IDisposable. /// Он либо вызывается программистом явно, либо неявно - из блока using() /// Microsoft нам говорит, что этот метод не надо делать виртуальным, но не говорит, почему. /// Скорее всего, просто чтоб не нарушился порядок вызова Dispose()/GC.SuppressFinalize(). /// public void Dispose() { //Вызовем оверлоад метода и скажем, что его вызвали явно: Dispose(true); //Отменим вызов деструктора для этого объекта - всё, что нужно, мы уже за собой убрали. //Это не критично (поскольку у нас есть защита от повторного вызова метода Dispose(bool) - флаг isDisposed), //но поможет нам оптимизировать производительность. GC.SuppressFinalize(this); } ////// Это деструктор, он же finalizer. /// Его вызывает Garbage Collector, когда фреймворк соизволит решить, что пора бы убраться. /// ~DisposableExample() { //Вызываем метод очистки, указывая, что он вызван из деструктора. //Если unmanaged ресурсов у нас нет, то тут "как бы" ничего нового не случится //и особого сакрального смысла в этом вызове нет, но лучше перебдеть, чем недобдеть. //Сюда мы придём только в том случае, если программист забыл уничтожить объект и не вызвал Dispose() Dispose(false); //Ничего больше в деструкторе быть не должно - весь код очистки ресурсов должен переехать в Dispose(bool) } ////// Очистка ресурсов. /// Чтобы не нарушить стройность логики в наследниках, этот метод не виртуальный, /// виртуальными сделаны два метода DisposeManaged() и DisposeUnmanaged(), которые вызываются внутри. /// /// /// True, если метод Dispose() вызывается явно, либо из блока using() /// False, когда метод Dispose() вызывается из деструктора (неявно, при сборке мусора). Нельзя обращаться к Managed-ресурсам, о них позаботится Garbage Collector /// В примере из MSDN параметр назван "disposing", что совершенно не проясняет его предназначение. /// protected void Dispose(bool isCalledExplicitly) { //Проверяем, что метод пока не вызывался: if(!isDisposed) { try { if(isCalledExplicitly) { //Dispose() был вызван явно программистом, //он хотел освободить память, поскольку этот объект уже не понадобится. //Garbage Collector до нашего объекта ещё не добрался, //поэтому мы сами освобождаем managed-ресурсы. DisposeManaged(); } //Освобождаем unmanaged-ресурсы, о них Garbage Collector-у никто не даст знать, //поэтому кроме нас их никто не освободит DisposeUnmanaged(); } finally { //Избегаем повторного вызова этого метода: isDisposed = true; } } } ////// Освобождение внутренних managed-объектов. /// Может быть перекрыт в наследниках с вызовом base.DisposeManaged() /// Если делается базовый класс, без наполнения метода функциональностью, /// то метод должен быть не protected, а abstract. /// protected virtual void DisposeManaged() { //Здесь надо уничтожить все managed-ресурсы //Все внутренние свойства, реализующие IDisposable, обрабатываются в этом методе: //this.DisposableProperty.Dispose(); } ////// Освобождение внутренних unmanaged-ресурсов /// Может быть перекрыт в наследниках с вызовом base.DisposeUnmanaged() /// Если делается базовый класс, без наполнения метода функциональностью, /// то метод должен быть не protected, а abstract. /// protected virtual void DisposeUnmanaged() { //Здесь очищаются ресурсы, о которых .Net Framework не знает, //например, открытые file handles: //CloseHandle(handle); } #endregion #region Дополнительные свойства и методы, которые могут пригодиться ////// Вернёт true, если объект уже уничтожен. /// Говорит о том, что этим объектом уже пользоваться не нужно. /// protected bool IsDisposed { get { return isDisposed; } } ////// Метод выбросит эксепшн, если у объекта был вызван метод Dispose(). /// CheckDisposed можно вставлять первой строкой в другие методы, /// которым важна целостность объекта. /// protected void CheckDisposed() { if(IsDisposed) throw new ObjectDisposedException(null); } #endregion }