C#: Зачем нужен Dispose и с чем его едят

Сегодня я поведую о том зачем нужна функция Dispose в языке программирования C#.

Не будем вдаваться в детали паттерна dispose, а так же не будем говорить о таком явлении как Finalizable. Рассмотрим только что такое Dispose и когда стоит его использовать.
Dispose — это некая команда, которая как бы помечает объект флагом, что мы больше не намерены использовать наш объект. По большей части это касается ситуаций когда мы работаем с классом Stream и его производными. Думаю все знакомы с классом StreamWriter. Поэтому давайте рассмотрим все на наглядном примере, попробовав реализовав простой класс Logger.

public class Logger
    {
        private System.IO.StreamWriter _sw = null;

        public Logger(string folderPath, Encoding encoding)
        {
            if(folderPath == null)
            {
                folderPath = "\\";
            }
            else if(folderPath == String.Empty || folderPath[folderPath.Length - 1] != '\\')
            {
                folderPath += "\\";
            }
            _sw = new System.IO.StreamWriter(folderPath + DateTime.Now.ToString("yyyy-MM-dd") + ".log", true, encoding);
        }

        protected string _GetLogString(string message)
        {
            return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ": " + message;
        }

        public void Write(string message)
        {
            _sw.Write(_GetLogString(message));
        }

        public void WriteLine(string message)
        {
            _sw.WriteLine(_GetLogString(message));
        }

        public void Close()
        {
            _sw.Dispose();
        }
    }

    /***Пример**/
    Logger l = new Logger("C:\\");
    l.WriteLine("Log string");
    l.Close();

Как и что работает в этом примере должно быть понятно. Но все таки есть вероятность что программист забудет вызвать метод Close. А, при данной реализации, это просто заблокирует лог файл в каталоге. Система будет думать, что он используется нашей программой. Ситуация не из лучших. Именно для того, чтобы избежать таких ситуаций была придумана директива using. Однако, чтобы использовать ее для нашего класса, нужно реализовать метод Dispose, а если быть точным интерфейс IDisposable.

public class Logger : IDisposable 
    {
        private System.IO.StreamWriter _sw = null;
        private bool _disposed = false; //флаг, что наш объект был Disposed

        public void Dispose()
        {
            Dispose(true); 
            GC.SuppressFinalize(this); //говорим сборщику мусора, что наш объект уже освободил ресурсы
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {   
                //нельзя вызвать метод Dispose для объекта дважды
                return;
            }
            if (disposing)
            {
                //тут освобождаем все ресурсы. В нашем случае он только один.
                Close(); //по сути делаем _sw.Dispose();
            }
            _disposed = true; //помечаем флаг что метод Dispose уже был вызван
        }

        public Logger(string folderPath, Encoding encoding)
        {
            if(folderPath == null)
            {
                folderPath = "\\";
            }
            else if(folderPath == String.Empty || folderPath[folderPath.Length - 1] != '\\')
            {
                folderPath += "\\";
            }
            _sw = new System.IO.StreamWriter(folderPath + DateTime.Now.ToString("yyyy-MM-dd") + ".log", true, encoding);
        }

        protected string _GetLogString(string message)
        {
            return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ": " + message;
        }

        public void Write(string message)
        {
            _sw.Write(_GetLogString(message));
        }

        public void WriteLine(string message)
        {
            _sw.WriteLine(_GetLogString(message));
        }

        public void Close()
        {
            _sw.Dispose();
        }
    }

После такой реализации класса мы можем использовать его в конструкции using. А так же правильно освобождать занятые класом ресурсы в любом удомном месте нашей программы.

Запись опубликована в рубрике C# с метками , , , , , , , , , , , . Добавьте в закладки постоянную ссылку.