Как стать автором
Обновить

Комментарии 24

Еще при чтении статьи у меня самого возник вопрос.

Логика третьего правила понятна — неуправляемые ресурсы должны быть по-любому освобождены, и если это не сделает программист с помощью Dispose, то в конце концов сборщик дернет финализатор.

А вот во втором правиле получается так, что если я Dispose забыл вызвать, то и ничего страшного?
Ну когда сборщик мусора доберется до объекта он его таки вызовет, но иногда может быть поздно.
Если вы в классе открыли файл для эксклюзивного доступа, а потом забыли вызывать Dispose и закрыть файл, то код, кому файл может понадобиться (например, для удаления его), может работать крайне нестабильно. В какие-то моменты сборщик успеет отработать и все будет ОК, а в какие-то вы получите ошибку.
Поэтому лучше все же за Dispose не забывать, чтобы потом не лепить костыли вроде
GC.Collect()
На GC надейся, а сам не плошай!
GC таки не вызывает Dispose. это работа программиста.
Да, действительно. Спасибо.
Но я везде где нужно реализую IDisposable и стараюсь использовать using. И как-то был в уверенности, что в принципе это не 100% обязательно, но в реальной жизни просто крайне желательно, чтобы не ловить странные ошибки доступа и прочее.
А это так и есть — не обязательно, но крайне желательно =)
Очень даже страшно не вызывать Dispose :-)
Используйте конструкцию using (...) {....} она сама вызовет Dispose
Зависит от ситуации =) На самом деле, просто нет никакого другого способа кроме IDisposable (ну и документации) указать пользователю класса что он может 'течь', так что создателю класса ничего не остается.
Weak Event, теперь Dispose Pattern… интересно конечно, но имхо как-то поздно все это описывать.
Да, замечание понятно. Но, скажем так, в любой момент найдется человек, незнакомый с таблицей умножения. Я сам, начав посещать Хабр год назад, очень много прочитал таких вот «поздних» тем и для меня они оказались очень полезны.
Продолжайте, лучше поздно чем никогда
Привет, вот он я.
Знания медленно устаревают, а новички быстро плодятся.
Так что спасибо.
Не за что!
«Это может вызвать исключение в потоке финализатора и нарушить работу всего приложения!»

Не понял, throwable в потоке финализатора рушит приложение? Это серьёзно так в .NET?
Это так начиная с версии 2.0.
IDisposable, кроме всего прочего, позволяет красиво организовывать работу с блоками кода.

Например следующий код:

using (new Log())
{
	// ...
	if (Services[INotifier]==null)
	{
		Log.Warn("Notifier not set!");
		return Log.Result(false);
	}
	else
	{
		// ...
	}
}


Создаёт лог с таким содержимым:

{ 2010-03-12 16:39:06Z Engine.Notify() [..\src\Engine.cs:126]
	! +0001 Notifier not set!
	= +0000 [Result]=[false]
} 0,0002 s / +12 288,00 b


Просто и красиво.

Другой вариант полезнюшки
public class ForceCulture : IDisposable
{
	public ForceCulture(CultureInfo culture)
	{
		//...
	}
	public ForceCulture() : this(CultureInfo.InvariantCulture)
	{
	}
	//...
}


В общем, идея, думаю понятна.
Кстати, если есть свои варианты использования — делитесь.
Где-то в этом же блоге была фишка с WaitCursor. В конструкторе меняешь курсор на часы, в Dispose — обратно. И оборачиваешь блок кода, на котором пользователь должен подождать, в using c этим WaitCursor'ом. Идея, думаю, понятна.
Использую ProgressBlock в IDisposable.
Изначально с marquee style.
В процессе можно обновлять (и отменять):

///<summary>
///Update progress bar
///</summary>
///<param name='current'>Current progress value</param>
///<param name='total'>Total progress value</param>
///<returns><c>False</c> for user cancel.</returns>
public bool UpdateProgress(long current, long total)


IMHO лучше чем WaitCursor :)
Огромное спасибо за статью, я как раз сейчас изучаю .NET и после вашей статьи наконец разобрался во всех Dispose(), финализаторах и прочем.
До сих пор представление о них у меня было весьма туманным)
Могу посоветовать еще вот эти скринкасты на тему памяти:
www.red-gate.com/products/ants_memory_profiler/DOTNET_Memory_Management/Index.html

(ANTS Memory Profiler, кстати, совершенно потрясающий инструмент)
Первое правило не совсем верное. При разработки библиотек имеет смысл у некоторых обектов реализовывать пустой Dispose, т.к. в будущем имплементация объекта может меняться, а несчатные пользователя билиотеки врядли сразу после выхода новой версии побегут менять свой код.Но это ИМХО.
Спасибо за статью.
А как насчет наследуемых классов? Если классы наследуются и в «среднем» появляется необходимость неуправляемого ресурса.
В базовом классе:
public void Dispose()
{
  Dispose(true);
}

public virtual void Dispose(bool disposing)
{
  if (disposing)
  {
    ...
  }
  ...
}


* This source code was highlighted with Source Code Highlighter.

В наследуемых классах переопределяем Dispose с параметром, в конце вызываем базовый:
public override void Dispose(bool disposing)
{
  if (disposing)
  {
   // освобождение своих ресурсов
  }
  base.Dispose(disposing);
}


* This source code was highlighted with Source Code Highlighter.
Блин, не дописал. В переопределяемом Dispose перед вызовом базового класса надо освободить неуправляемый ресурс. Также необходимо добавить финализатор и в нем вызвать Dispose(false).
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории