Использование IDisposable для отмены подписки на событие - нужно ли помещать другие вещи в распоряжение? - PullRequest
8 голосов
/ 13 января 2010

У меня есть следующий класс

public class Presenter: IDisposable
{
   public IView View
   {get;private set;}

   //snip other object reference, all managed
   public Presenter(IView view)
  {
     View=view;
     View.MouseUp += MouseUpEvent;
  }

  public void MouseUpEvent()
  {
    //do whatever you want to do on mouse up
  }

  public void Dispose()
  {
    View.MouseUp -= MouseUpEvent;
    // no more manual disposing
  }
}

Вопрос теперь в том, правильно ли я реализую метод Dispose()? Или мне нужно вручную удалить все другие управляемые объекты только потому, что у меня есть explicilty define Dispose()?

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

Ответы [ 2 ]

8 голосов
/ 13 января 2010

Если вы выберете подписку в конструкторе, это выглядит разумно. Я бы повторил мнение Джоша, что это не лучший подход. С другой стороны, это может быть самый простой способ достижения вашей цели, что всегда хорошо. Я не собираюсь притворяться экспертом по шаблонам пользовательского интерфейса: подняв вопрос, я подумаю, что именно так вы хотите работать, и решу сам вопрос:)

Мне лично кажется, что предписанный Джошем шаблон 1 слишком сложен для простых сценариев - ваш подход хорош, с одним изменением: сделайте свой класс запечатанным. Если вы не не хотите запечатать класс, вам следует выбрать опцию Dispose(bool) (но без финализатора), потому что подклассам также может потребоваться избавиться от вещей, и может потребоваться финализатор. Без возможности получения производного типа жизнь проще (как это часто бывает).

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

Итак, вам нужно извлечь что-то еще из этого класса?


1 Я понимаю, что это стандартная рекомендуемая схема, хотя я бы порекомендовал вам прочитать совет Джо Даффи и др. для получения более подробной информации - все может получить очень сложный.

5 голосов
/ 13 января 2010

Лично я бы не стал зацеплять / отцеплять событие в конструкторе и избавляться. Вместо этого я бы добавил код в методы доступа get / set View и добавил их туда. Но если докладчик удаляется, когда привязан вид, я бы не стал пытаться его очистить. Вы можете явно отсоединить представление от докладчика, если вам необходимо явное отсоединение.

Сказав это, вот что я знаю о IDisposable.

Рекомендуемый подход к реализации IDisposable заключается в использовании защищенного метода Dispose (bool), в котором вы выполняете действие. Причина в том, что вы хотите различать явное удаление и удаление, вызванное завершением (сборкой мусора).

Когда вы удаляетесь из-за явного вызова Dispose (), можно трогать управляемые объекты, и вы должны утилизировать все, что вы создали, что также требует удаления. Таким образом, вы делаете это только при удалении = true.

Но если кто-то (вы) забывает вызвать Dispose и вызывается финализатор, вы удаляетесь после сборки мусора (dising = false) и не хотите трогать какие-либо управляемые объекты, потому что они могут быть уже завершены. В этом случае вам нужно освободить только неуправляемые ресурсы, такие как дескрипторы Win32 и т. Д.

Наконец, когда явно вызывается Dispose (), вы заметите, что я вызвал GC.SupressFinalize (this), который является подсказкой производительности для сборщика мусора. Это позволяет ему знать, что объект не должен быть завершен, когда он собран. Завершение не дешево.

class MyObject : IDisposable {

    ~MyObject() {
        Dispose(false);
    }

    public void Dispose() {
        Dispose(true);
        GC.SupressFinalize(this);
    }

    protected virtual void Dispose(bool disposing) {
        if (disposing) {
            // dispose of managed resources
        }
        // dispose of unmanaged resources
    }

}
...