Как правильно реализовать IDisposable

Я видел так много кода С# в свое время как разработчика, который пытается помочь GC, установив переменные в null или вызывая Dispose() для классов (например, DataSet) в своих собственных классах. Метод Dispose(), который Мне было интересно, есть ли необходимость реализовать его в управляемой среде.

Является ли этот код пустой тратой времени в шаблоне проектирования?

class MyClass : IDisposable 
{
    #region IDisposable Members

    public void Dispose() 
    {
        otherVariable = null;
        if (dataSet != null)
        {
            dataSet.Dispose();
        }
    }

    #endregion
}

Ответ 1

GC не вызывает .Dispose() (однако он вызывает метод finalize ~MyClass(), который вы можете предоставить вызову методу Dispose() для автоматического управления ресурсами, когда GC решает очистить ваш класс).

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

От MSDN:

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

public void Dispose()
{
    otherVariable = null;
    if (dataSet != null)
    {
        dataSet.Dispose();
        dataSet = null;
    }
}

Ответ 2

Нет, утилизировать методы не пустая трата времени.

Упорядочить шаблон, чтобы позволить вызывающему пользователю очистить класс, как только они закончили с ним, вместо того, чтобы ждать, пока GC его соберет. Задержка не имеет большого значения для обычной памяти кучи, поэтому базовые классы, такие как String, не реализуют ее. Однако Dispose полезен для очистки неуправляемых ресурсов. Где-то внутри, класс Dataset использует неуправляемый ресурс, поэтому он предоставляет метод dispose, позволяющий вам сообщать, когда этот неуправляемый ресурс может быть освобожден.

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

Как установить переменные в null в методе dispose. Почти во всех случаях это было бы бессмысленно. установка переменной в нуль удаляет ссылку на эту переменную, что сделает ее пригодной для сбора мусора (если это последняя ссылка), но, поскольку вы все равно распоряжаетесь классом, вы, скорее всего, выйдете из области для содержания класс, поэтому внутренний класс станет доступным для сбора в любом случае.

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

Ответ 3

Не совсем. Если у вас есть переменные-члены, которые являются одноразовыми, то вы, вероятно, должны распоряжаться им таким образом. Ваш объект может прожить дольше, чем объем выполняемой работы, так как сборщик мусора не может запускаться в любой конкретный момент времени.

Установка управляемых переменных в null является пустой тратой времени. Объект не будет получать GC'd быстрее.

Ответ 4

Мусоровоз приходит в мою область каждую неделю, но он не собирает мой мусор, если я не стану мусорным ящиком таким образом, чтобы он мог собирать.

Вы должны просто удалить все нежелательные подписки на события, ссылки и очистить неуправляемые обработчики. Тогда сборщик мусора позаботится об остальном.

Ниже приведен пример общей передовой практики для реализации интерфейса IDisposable. Ссылка: https://msdn.microsoft.com/en-us/library/system.idisposable.dispose(v=vs.110).aspx

public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;

        // The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

        // Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}