29 августа 2014 г.

Частая ошибка №8: Пренебрежение очисткой ресурсов. Перевод.

Оглавление

Среда CLR использует сборщик мусора, так что вам не надо явно освобождать память, занятую под любой объект. Фактически, вы и не можете. Нет, подобного C++, оператора delete или функции free() как в C. Но это не означает что вы можете просто забыть обо всех объектах, после того как закончили использовать их. Многие типы объектов включают в себя некоторые другие типы системных ресурсов (например файл на диске, подключение к БД, сетевое соединение и т.п.). Оставляя такие ресурсы открытыми вы можете быстро истощить общее количество системных ресурсов, снизить производительность и в конце концов привести программу к ошибке.
Хотя метод деструктора может быть объявлен в любом C# классе, проблема с деструкторами (так же называемыми финализаторами) в том, что вы не знаете точно когда он будет вызван. Они вызываются сборщиком мусора (в отдельном потоке, что может вызвать дополнительными осложнения) в неопределенное время. Попытка обойти эти ограничения, вызвав принудительную сборку мусора - GC.Collect(), не является хорошей практикой, так как может заблокировать поток на неопределенное время, пока он собирает все объекты, доступные для сборки.

Это не означает что у финализаторов нет хороших вариантов использования, но освобождение ресурсов детерминированным способом точно не один из них. А вот когда вы работаете с файлом, сетевым подключением или подключением к БД вы хотите точно освободить этот ресурс, как только закончите с ним.

Утечка ресурсов - это проблема практически в любой среде. Тем не менее, C# предоставляет ясный и простой в использовании механизм, использование которого может минимизировать количество таких утечек. В .NET Framework определен интерфейс IDisposable, который содержит только один метод - Dispose(). Любой объект, который реализует IDisposable "ожидает" что этот метод будет вызван когда пользователь закончит работу с данным объектом. Так мы получаем явное, детерминированное, освобождение ресурсов.

Если вы создаете и финализируете объект в контексте одного блока кода, то практически непростительно забыть вызвать метод Dispose(), потому что C# предоставляет оператор using, который обеспечит вызов Dispose() вне зависимости от того как этот блок кода будет завершен (будь то исключение, оператор возврата или просто закрытие блока). И да, это тот же самый оператор using, упомянутый ранее, который используется для включения пространств имен в начале вашего файла. Он имеет второе, совершенно несвязанное предназначение, о котором многие C# разработчики не знают, а именно гарантировать что Dispose() будет вызван для объекта, когда завершится блок кода:


Создавая блок кода using в примере выше, вы точно знаете что myFile.Dispose() будет вызван как только вы закончите с файлом, даже если Read() выбросит исключение.

Комментариев нет:

Отправить комментарий