Правильное использование интерфейса IDisposable - PullRequest
1521 голосов
/ 11 февраля 2009

Из чтения документации Microsoft я знаю, что "основное" использование интерфейса IDisposable заключается в очистке неуправляемых ресурсов.

Для меня «неуправляемый» означает такие вещи, как подключения к базе данных, сокеты, дескрипторы окон и т. Д. Но я видел код, в котором реализован метод Dispose() для освобождения управляемых ресурсов, что выглядит избыточно для меня, так как сборщик мусора должен позаботиться об этом за вас.

Например:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }

Мой вопрос: делает ли сборщик мусора свободную память, используемую MyCollection, быстрее, чем обычно?

edit : Пока что люди опубликовали несколько хороших примеров использования IDisposable для очистки неуправляемых ресурсов, таких как соединения с базой данных и растровые изображения. Но предположим, что _theList в приведенном выше коде содержит миллион строк, и вы хотели освободить эту память сейчас , а не ждать сборщика мусора. Будет ли код, приведенный выше, выполнить это?

Ответы [ 19 ]

5 голосов
/ 15 июня 2013

Помимо основного использования в качестве способа управления временем жизни из системных ресурсов (полностью охвачено удивительным ответом Ian , слава!), IDisposable / using комбо также можно использовать для определения области изменения (критических) глобальных ресурсов : консоль , потоки процесс , любой глобальный объект , такой как экземпляр приложения .

Я написал статью об этом паттерне: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/

Это иллюстрирует, как вы можете защитить некоторые часто используемые глобальные состояния в многоразовых и читаемых способом: консольных цветах , текущем нить культуры , Свойства объекта приложения Excel ...

4 голосов
/ 11 февраля 2009

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

Вызывать методы Clear () не нужно, и GC, вероятно, не сделал бы этого, если бы Dispose этого не сделал ...

2 голосов
/ 26 мая 2017

Ваш пример кода не является хорошим примером для IDisposable использования. Очистка словаря обычно не должна переходить на метод Dispose. Элементы словаря будут очищены и удалены, когда они выйдут из области видимости. * Требуется реализация IDisposable, чтобы освободить некоторые памяти / обработчики, которые не будут освобождаться / освобождаться даже после того, как они выйдут из области видимости.

В следующем примере показан хороший пример шаблона IDisposable с некоторым кодом и комментариями.

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's 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.
    }
}
2 голосов
/ 14 сентября 2016

Наиболее оправданным вариантом использования для удаления управляемых ресурсов является подготовка к GC для восстановления ресурсов, которые в противном случае никогда бы не были собраны.

Ярким примером являются циклические ссылки.

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

Единственный способ обойти это - вручную разорвать циклические ссылки, установив родительские ссылки в null для дочерних элементов.

Реализация IDisposable для родителей и детей - лучший способ сделать это. Когда Dispose вызывается для родительского элемента, вызывается Dispose для всех дочерних элементов, а в дочернем методе Dispose установите для родительских ссылок значение null.

2 голосов
/ 31 августа 2015

Первое определение. Для меня неуправляемый ресурс означает некоторый класс, который реализует интерфейс IDisposable или что-то, созданное с использованием вызовов в dll. GC не знает, как обращаться с такими объектами. Если класс имеет, например, только типы значений, то я не рассматриваю этот класс как класс с неуправляемыми ресурсами. Для своего кода я следую следующим практикам:

  1. Если созданный мной класс использует некоторые неуправляемые ресурсы, то это означает, что я должен также реализовать интерфейс IDisposable для очистки памяти.
  2. Очистите объекты, как только я закончу их использовать.
  3. В моем методе dispose я перебираю все IDisposable члены класса и вызываю Dispose.
  4. В моем методе Dispose вызовите GC.SuppressFinalize (this), чтобы уведомить сборщик мусора о том, что мой объект уже очищен. Я делаю это потому, что вызов GC - дорогая операция.
  5. В качестве дополнительной меры предосторожности я пытаюсь сделать возможным вызов Dispose () несколько раз.
  6. Иногда я добавляю закрытый член _disposed и проверяю вызовы методов, когда объект был очищен. И если он был очищен, то сгенерируйте ObjectDisposedException
    Следующий шаблон демонстрирует то, что я описал словами как пример кода:

public class SomeClass : IDisposable
    {
        /// <summary>
        /// As usually I don't care was object disposed or not
        /// </summary>
        public void SomeMethod()
        {
            if (_disposed)
                throw new ObjectDisposedException("SomeClass instance been disposed");
        }

        public void Dispose()
        {
            Dispose(true);
        }

        private bool _disposed;

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;
            if (disposing)//we are in the first call
            {
            }
            _disposed = true;
        }
    }
2 голосов
/ 11 февраля 2009

В коде примера есть операция Dispose(), которая может иметь эффект, который не возникает из-за обычного GC объекта MyCollection.

Если объекты, на которые ссылаются _theList или _theDict, ссылаются на другие объекты, то этот объект List<> или Dictionary<> не будет подлежать сбору, но внезапно не будет иметь содержимого. Если бы не было операции Dispose (), как в примере, эти коллекции все равно содержали бы свое содержимое.

Конечно, если бы это была ситуация, я бы назвал это ломанным дизайном - я просто указываю (педантично, я полагаю), что операция Dispose() может быть не полностью избыточной, в зависимости от того, есть ли другие применения List<> или Dictionary<>, которые не показаны во фрагменте.

2 голосов
/ 22 февраля 2012

Одна из проблем большинства дискуссий о «неуправляемых ресурсах» заключается в том, что они на самом деле не определяют термин, но, похоже, подразумевают, что он как-то связан с неуправляемым кодом. Хотя верно, что многие типы неуправляемых ресурсов взаимодействуют с неуправляемым кодом, думать о неуправляемых ресурсах в таких терминах бесполезно.

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

Таким образом, неуправляемый ресурс представляет собой соглашение какой-то внешней «вещи» об изменении своего поведения от имени объекта, что бесполезно ухудшает полезность этой внешней «вещи», если объект был оставлен и прекратил существование. Управляемый ресурс - это объект, который является бенефициаром такого соглашения, но который подписался на получение уведомления об отказе от него и который будет использовать такое уведомление для наведения порядка в своих делах до его уничтожения.

2 голосов
/ 20 февраля 2012

IDisposable подходит для отмены подписки на события.

0 голосов
/ 03 октября 2018

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

https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

По актуальному вопросу; Если вы используете IDisposable для очистки управляемых объектов, которые занимают много памяти, короткий ответ будет нет . Причина в том, что после удаления IDisposable вы должны позволить ему выйти из области видимости. На этом этапе любые дочерние объекты, на которые ссылаются, также находятся вне области видимости и будут собраны.

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

...