Реализация интерфейса IDisposable на c#
Когда-то давно я разобрался в и так несложном интерфейсе IDisposable и сделал "правильный" пример реализации, по сути, просто откомментировав код с MSDN, для лучшего понимания, зачем и почему нужно делать именно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | class DisposableExample: IDisposable { #region Абсолютный минимум кода, который должен быть написан в правильной реализации IDisposable /// <summary> /// Поле, где будет true, если объект уже пытались уничтожить. /// </summary> private bool isDisposed = false ; /// <summary> /// Этот метод реализует интерфейс IDisposable. /// Он либо вызывается программистом явно, либо неявно - из блока using() /// Microsoft нам говорит, что этот метод не надо делать виртуальным, но не говорит, почему. /// Скорее всего, просто чтоб не нарушился порядок вызова Dispose()/GC.SuppressFinalize(). /// </summary> public void Dispose() { //Вызовем оверлоад метода и скажем, что его вызвали явно: Dispose( true ); //Отменим вызов деструктора для этого объекта - всё, что нужно, мы уже за собой убрали. //Это не критично (поскольку у нас есть защита от повторного вызова метода Dispose(bool) - флаг isDisposed), //но поможет нам оптимизировать производительность. GC.SuppressFinalize( this ); } /// <summary> /// Это деструктор, он же finalizer. /// Его вызывает Garbage Collector, когда фреймворк соизволит решить, что пора бы убраться. /// </summary> ~DisposableExample() { //Вызываем метод очистки, указывая, что он вызван из деструктора. //Если unmanaged ресурсов у нас нет, то тут "как бы" ничего нового не случится //и особого сакрального смысла в этом вызове нет, но лучше перебдеть, чем недобдеть. //Сюда мы придём только в том случае, если программист забыл уничтожить объект и не вызвал Dispose() Dispose( false ); //Ничего больше в деструкторе быть не должно - весь код очистки ресурсов должен переехать в Dispose(bool) } /// <summary> /// Очистка ресурсов. /// Чтобы не нарушить стройность логики в наследниках, этот метод не виртуальный, /// виртуальными сделаны два метода DisposeManaged() и DisposeUnmanaged(), которые вызываются внутри. /// </summary> /// <param name="isCalledExplicitly"> /// 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 ; } } } /// <summary> /// Освобождение внутренних managed-объектов. /// Может быть перекрыт в наследниках с вызовом base.DisposeManaged() /// Если делается базовый класс, без наполнения метода функциональностью, /// то метод должен быть не protected, а abstract. /// </summary> protected virtual void DisposeManaged() { //Здесь надо уничтожить все managed-ресурсы //Все внутренние свойства, реализующие IDisposable, обрабатываются в этом методе: //this.DisposableProperty.Dispose(); } /// <summary> /// Освобождение внутренних unmanaged-ресурсов /// Может быть перекрыт в наследниках с вызовом base.DisposeUnmanaged() /// Если делается базовый класс, без наполнения метода функциональностью, /// то метод должен быть не protected, а abstract. /// </summary> protected virtual void DisposeUnmanaged() { //Здесь очищаются ресурсы, о которых .Net Framework не знает, //например, открытые file handles: //CloseHandle(handle); } #endregion #region Дополнительные свойства и методы, которые могут пригодиться /// <summary> /// Вернёт true, если объект уже уничтожен. /// Говорит о том, что этим объектом уже пользоваться не нужно. /// </summary> protected bool IsDisposed { get { return isDisposed; } } /// <summary> /// Метод выбросит эксепшн, если у объекта был вызван метод Dispose(). /// CheckDisposed можно вставлять первой строкой в другие методы, /// которым важна целостность объекта. /// </summary> protected void CheckDisposed() { if (IsDisposed) throw new ObjectDisposedException( null ); } #endregion } |