Правильное использование интерфейса IDisposable

Из документации 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быстрее, чем обычно?

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

11.02.2009 18:12:41
Мне нравится принятый ответ, потому что он говорит вам правильный «шаблон» использования IDisposable, но, как сказал ОП в своем редактировании, он не отвечает на его предполагаемый вопрос. IDisposable не «вызывает» GC, он просто «помечает» объект как разрушаемый. Но каков реальный способ освободить память «прямо сейчас» вместо того, чтобы ждать, пока GC начнет действовать? Я думаю, что этот вопрос заслуживает большего обсуждения.
Punit Vora 1.10.2010 17:15:33
IDisposableничего не помечает. DisposeМетод делает то , что он должен сделать , чтобы очистить ресурсы , используемые экземпляром. Это не имеет ничего общего с GC.
John Saunders 1.10.2010 19:37:12
@John. Я понимаю IDisposable. И именно поэтому я сказал, что принятый ответ не отвечает на заданный OP вопрос (и последующее редактирование) о том, поможет ли IDisposable в <i> освобождении памяти </ i>. Так IDisposableкак не имеет никакого отношения к освобождению памяти, а только к ресурсам, то, как вы сказали, нет нужды вообще устанавливать для управляемых ссылок значение null, как это делал OP в своем примере. Таким образом, правильный ответ на его вопрос: «Нет, это не помогает освободить память быстрее. Фактически, это не помогает освободить память вообще, только ресурсы». Но в любом случае, спасибо за ваш вклад.
Punit Vora 7.10.2010 15:17:04
@desigeek: если это так, то вам не следовало говорить «IDisposable не вызывает» GC, он просто «помечает» объект как разрушаемый »
John Saunders 7.10.2010 15:31:05
@desigeek: не существует гарантированного способа освобождения памяти детерминистически. Вы можете вызвать GC.Collect (), но это вежливый запрос, а не требование. Все выполняющиеся потоки должны быть приостановлены для продолжения сборки мусора - прочитайте концепцию безопасных точек .NET, если вы хотите узнать больше, например, msdn.microsoft.com/en-us/library/678ysw69(v=vs.110). aspx . Если поток не может быть приостановлен, например, из-за вызова неуправляемого кода, GC.Collect () может вообще ничего не делать.
Concrete Gannet 14.08.2015 05:15:34
19 ОТВЕТОВ
РЕШЕНИЕ

Задача Dispose - освободить неуправляемые ресурсы. Это нужно сделать в какой-то момент, иначе они никогда не будут очищены. Сборщик мусора не знает, как вызвать DeleteHandle()переменную типа IntPtr, он не знает, нужно ли ему вызывать DeleteHandle().

Примечание . Что такое неуправляемый ресурс ? Если вы нашли его в Microsoft .NET Framework: он управляется. Если вы сами ковырялись в MSDN, это неуправляемо. Все, что вы использовали с помощью вызовов P / Invoke, чтобы выйти из приятного удобного мира всего, что доступно вам в .NET Framework, неуправляемо - и теперь вы несете ответственность за его очистку.

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

public void Cleanup()

или

public void Shutdown()

Но вместо этого есть стандартизированное имя для этого метода:

public void Dispose()

Был даже создан интерфейс IDisposable, который имеет только один метод:

public interface IDisposable
{
   void Dispose()
}

Таким образом, вы заставляете ваш объект предоставлять IDisposableинтерфейс, и таким образом вы обещаете, что написали этот единственный метод для очистки ваших неуправляемых ресурсов:

public void Dispose()
{
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}

И вы сделали. За исключением того, что вы можете сделать лучше.


Что если ваш объект выделил 250MB System.Drawing.Bitmap (то есть, управляемый .NET класс Bitmap) в качестве буфера кадра? Конечно, это управляемый объект .NET, и сборщик мусора освободит его. Но вы действительно хотите оставить 250 МБ памяти, просто сидя там - ожидая, когда сборщик мусора в конце концов придет и освободит его? Что если есть открытое соединение с базой данных ? Конечно, мы не хотим, чтобы это соединение оставалось открытым, ожидая, пока GC завершит объект.

Если пользователь вызвал Dispose()(то есть он больше не планирует использовать объект), почему бы не избавиться от этих расточительных растровых изображений и соединений с базой данных?

Итак, теперь мы будем:

  • избавиться от неуправляемых ресурсов (потому что мы должны), и
  • избавиться от управляемых ресурсов (потому что мы хотим быть полезными)

Итак, давайте обновим наш Dispose()метод, чтобы избавиться от этих управляемых объектов:

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose();
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose();
      this.frameBufferImage = null;
   }
}

И все хорошо, кроме того, что вы можете сделать лучше !


Что делать, если человек забыл позвонить Dispose()на ваш объект? Тогда они будут пропускать некоторые неуправляемые ресурсы!

Примечание. Они не будут пропускать управляемые ресурсы, поскольку в конечном итоге сборщик мусора будет работать в фоновом потоке и освобождать память, связанную с любыми неиспользуемыми объектами. Это будет включать ваш объект и любые управляемые объекты, которые вы используете (например, Bitmapи DbConnection).

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

Примечание. Сборщик мусора в конечном итоге освободит все управляемые объекты. Когда это происходит, он вызывает Finalize метод объекта. GC не знает или не заботится о вашем методе утилизации . Это было просто имя, которое мы выбрали для метода, который мы вызываем, когда хотим избавиться от неуправляемых вещей.

Уничтожение нашего объекта сборщиком мусора - идеальное время, чтобы освободить эти надоедливые неуправляемые ресурсы. Мы делаем это путем переопределения Finalize()метода.

Примечание. В C # вы явно не переопределяете Finalize()метод. Вы пишете метод , который выглядит как C ++ деструктора , и компилятор принимает , что быть вашей реализацией Finalize()методы:

~MyObject()
{
    //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
    Dispose(); //<--Warning: subtle bug! Keep reading!
}

Но в этом коде есть ошибка. Вы видите, сборщик мусора работает в фоновом потоке ; Вы не знаете порядок, в котором уничтожены два объекта. Вполне возможно, что в вашем Dispose()коде управляемого объекта, от которого вы пытаетесь избавиться (потому что вы хотели быть полезным), больше нет:

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
      this.frameBufferImage = null;
   }
}

Так что вам нужен способ Finalize()сказать, Dispose()что он не должен касаться каких-либо управляемых ресурсов (потому что их там больше не может быть ), при этом все еще освобождая неуправляемые ресурсы.

Стандартный шаблон для этого - иметь Finalize()и Dispose()вызывать третий (!) Метод; где вы передаете логическое выражение, если вы вызываете его Dispose()(в отличие от Finalize()), то есть безопасно освобождать управляемые ресурсы.

Этот внутренний метод может иметь произвольное имя, например «CoreDispose» или «MyInternalDispose», но традиционно его называют Dispose(Boolean):

protected void Dispose(Boolean disposing)

Но более полезное имя параметра может быть:

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too, but only if I'm being called from Dispose
   //(If I'm being called from Finalize then the objects might not exist
   //anymore
   if (itIsSafeToAlsoFreeManagedObjects)  
   {    
      if (this.databaseConnection != null)
      {
         this.databaseConnection.Dispose();
         this.databaseConnection = null;
      }
      if (this.frameBufferImage != null)
      {
         this.frameBufferImage.Dispose();
         this.frameBufferImage = null;
      }
   }
}

И вы измените свою реализацию IDisposable.Dispose()метода на:

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
}

и ваш финализатор для:

~MyObject()
{
   Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}

Примечание : если ваш объект происходит от объекта, который реализует Dispose, то не забывайте вызывать их базовый метод Dispose при переопределении Dispose:

public override void Dispose()
{
    try
    {
        Dispose(true); //true: safe to free managed resources
    }
    finally
    {
        base.Dispose();
    }
}

И все хорошо, кроме того, что вы можете сделать лучше !


Если пользователь вызывает Dispose()ваш объект, то все было убрано. Позже, когда придет сборщик мусора и вызовет Finalize, он снова вызовет Dispose.

Мало того, что это расточительно, но если ваш объект имеет ненужные ссылки на объекты, которые вы уже удалили из последнего вызова Dispose(), вы попытаетесь утилизировать их снова!

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

Когда пользователь вызывает Dispose(): дескриптор CursorFileBitmapIconServiceHandle уничтожается. Позже, когда запускается сборщик мусора, он снова попытается уничтожить ту же ручку.

protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy 
   ...
}

Способ исправить это - сказать сборщику мусора, что ему не нужно беспокоиться о завершении объекта - его ресурсы уже очищены, и больше не требуется никакой работы. Вы можете сделать это по телефону GC.SuppressFinalize()в Dispose()методе:

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
   GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}

Теперь, когда пользователь позвонил Dispose(), мы имеем:

  • освобожденные неуправляемые ресурсы
  • освобожденные управляемые ресурсы

В GC нет смысла запускать финализатор - обо всем позаботились.

Не могу ли я использовать Finalize для очистки неуправляемых ресурсов?

Документация для Object.Finalizeговорит:

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

Но в документации MSDN также сказано IDisposable.Dispose:

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

Так что это? Какое место для меня, чтобы очистить неуправляемые ресурсы? Ответ:

Это твой выбор! Но выбирай Dispose.

Вы, конечно, можете поместить свою неуправляемую очистку в финализатор:

~MyObject()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //A C# destructor automatically calls the destructor of its base class.
}

Проблема в том, что вы понятия не имеете, когда сборщик мусора дойдет до завершения вашего объекта. Ваш непредоставленные удалось, не-необходимости, Неиспользуемая родные ресурсы будут придерживаться вокруг , пока сборщик мусора в конечном счете не работает. Затем он вызовет ваш метод финализатора; очистка неуправляемых ресурсов. Документация Object.Finalize указывает на это:

Точное время выполнения финализатора не определено. Чтобы обеспечить детерминированное освобождение ресурсов для экземпляров вашего класса, реализуйте метод Close или предоставьте IDisposable.Disposeреализацию.

Это преимущество использования Disposeдля очистки неуправляемых ресурсов; Вы узнаете и контролируете, когда неуправляемые ресурсы очищаются. Их уничтожение является «детерминированным» .


Чтобы ответить на ваш первоначальный вопрос: почему бы не освободить память сейчас, а не тогда, когда GC решит это сделать? У меня есть программное обеспечение для распознавания лиц, которое теперь должно избавиться от 530 МБ внутренних изображений , поскольку они больше не нужны. Когда мы этого не делаем: машина останавливается.

Бонус Чтение

Для тех, кто любит стиль этого ответа (объясняя почему , и как это становится очевидным), я предлагаю вам прочитать Главу 1 «Основы COM» Don Box:

На 35 страницах он объясняет проблемы использования бинарных объектов и изобретает COM на ваших глазах. Как только вы поймете причину COM, оставшиеся 300 страниц станут очевидными и просто детализируют реализацию Microsoft.

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

Дополнительное чтение бонусов

Когда все, что ты знаешь, неправильно Эриком Липпертом

Поэтому действительно очень трудно написать правильный финализатор, и лучший совет, который я могу дать, - не пытаться .

2592
1.03.2019 16:13:48
Вы можете сделать лучше - вам нужно добавить вызов GC.SuppressFinalize () в Dispose.
plinth 12.02.2009 21:29:12
@Daniel Earwicker: это правда. Microsoft хотела бы, чтобы вы вообще прекратили использовать Win32 и придерживались красиво абстрагируемых, портативных, независимых от устройств вызовов .NET Framework. Если вы хотите покопаться в операционной системе под ней; потому что вы думаете, что знаете, какая ОС работает: вы берете свою жизнь в свои руки. Не каждое приложение .NET работает на Windows или на рабочем столе.
Ian Boyd 2.04.2010 12:53:40
Это отличный ответ, но я думаю, что он выиграл бы от окончательного перечисления кода для стандартного случая и для случая, когда класс является производным от базового класса, который уже реализует Dispose. например, прочитав здесь ( msdn.microsoft.com/en-us/library/aa720161%28v=vs.71%29.aspx ), я также запутался в том, что мне следует делать при выводе из класса, который уже реализует Dispose ( эй, я новичок в этом).
integra753 9.02.2012 12:42:08
@GregS и другие: как правило, я бы не стал задавать ссылки на null. Во-первых, это означает, что вы не можете их сделать readonly, а во-вторых, вы должны делать очень уродливые !=nullпроверки (как в примере кода). У вас может быть флаг disposed, но легче об этом не беспокоиться. .NET GC достаточно агрессивен, так что ссылка на поле xбольше не будет считаться «использованной» к тому времени, когда она пройдет x.Dispose()строку.
porges 22.02.2012 06:52:24
На второй странице книги Дона Бокса, которую вы упомянули, он использует пример реализации алгоритма поиска O (1), «детали которого оставлены в качестве упражнения для читателя». Я смеялся.
wip 29.10.2013 07:52:04

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

Ответ на ваши изменения: вроде. Если я сделаю это:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

Это функционально идентично этому для целей управления памятью:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

Если вам действительно очень нужно освободить память в этот самый момент, звоните GC.Collect(). Там нет причин делать это здесь, хотя. Память будет освобождена, когда это необходимо.

11
11.02.2009 21:17:47
Re: «Память будет освобождена, когда это необходимо.» Скорее сказать, «когда GC решит, что это необходимо». Вы можете увидеть проблемы с производительностью системы, прежде чем GC решит, что память действительно нужна. Освободить его сейчас не обязательно, но может быть полезно.
Jesse Chisholm 30.08.2012 23:40:33
В некоторых угловых случаях удаление ссылок в коллекции может ускорить сборку мусора для элементов, на которые они ссылаются. Например, если большой массив создается и заполняется ссылками на новые объекты меньшего размера, но это не требуется очень долго после этого, отказ от массива может привести к тому, что эти элементы будут храниться до следующего уровня 2 GC, в то время как его обнуление первым может сделать предметы подходящими для следующего уровня 0 или уровня 1 GC. Конечно, иметь большие недолговечные объекты в куче больших объектов в любом случае нелепо (мне не нравится дизайн), но ...
supercat 26.04.2013 15:48:48
... обнуление таких массивов перед тем, как оставить их, иногда уменьшает влияние GC.
supercat 26.04.2013 15:49:12

IDisposableчасто используется для использования usingоператора и использования простого способа детерминированной очистки управляемых объектов.

public class LoggingContext : IDisposable {
    public Finicky(string name) {
        Log.Write("Entering Log Context {0}", name);
        Log.Indent();
    }
    public void Dispose() {
        Log.Outdent();
    }

    public static void Main() {
        Log.Write("Some initial stuff.");
        try {
            using(new LoggingContext()) {
                Log.Write("Some stuff inside the context.");
                throw new Exception();
            }
        } catch {
            Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
        } finally {
            Log.Write("Some final stuff.");
        }
    }
}
64
11.02.2009 18:20:41
Лично мне это нравится, но на самом деле это не совпадает с рекомендациями по проектированию фреймворка.
mqp 11.02.2009 18:22:49
Я бы посчитал это правильным дизайном, потому что он позволяет легко детерминировать области видимости и создавать / очищать области видимости, особенно когда он сложным образом смешивается с обработкой исключений, блокировкой и использованием неуправляемых ресурсов. Язык предлагает это как первоклассную функцию.
yfeldblum 11.02.2009 18:30:19
Он не совсем соответствует правилам, указанным в FDG, но, безусловно, является допустимым использованием шаблона, так как он требуется для использования «оператором использования».
Scott Dorman 11.02.2009 18:44:35
Пока Log.Outdent не выбрасывает, в этом нет ничего плохого.
Daniel Earwicker 11.02.2009 19:51:07
Различные ответы на вопрос: является ли неправомерным использование IDisposable и «использование» в качестве средства получения «ограниченного поведения» для обеспечения безопасности исключений? подробнее рассмотрим, почему разные люди любят / не любят эту технику. Это несколько спорный.
Brian 13.10.2017 14:46:42

Если MyCollectionвы все равно будете собирать мусор, вам не нужно его утилизировать. Это просто увеличит производительность ЦП, чем необходимо, и может даже сделать недействительным некоторый предварительно рассчитанный анализ, который сборщик мусора уже выполнил.

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

РЕДАКТИРОВАТЬ В ответ на комментарий Скотта:

Единственный раз, когда затрагиваются показатели производительности GC, это когда вызывается [sic] GC.Collect ()

Концептуально GC поддерживает представление графа ссылок объекта и всех ссылок на него из стековых фреймов потоков. Эта куча может быть довольно большой и занимать много страниц памяти. В качестве оптимизации GC кэширует свой анализ страниц, которые вряд ли будут меняться очень часто, чтобы избежать ненужного повторного сканирования страницы. GC получает уведомление от ядра при изменении данных на странице, поэтому он знает, что страница грязная и требует повторного сканирования. Если коллекция находится в Gen0, то вероятно, что другие вещи на странице тоже меняются, но это менее вероятно в Gen1 и Gen2. Как ни странно, эти перехватчики не были доступны в Mac OS X для команды, которая перенесла GC на Mac, чтобы заставить плагин Silverlight работать на этой платформе.

Еще один момент против ненужной утилизации ресурсов: представьте ситуацию, когда процесс выгружается. Представьте также, что процесс запущен в течение некоторого времени. Скорее всего, многие страницы памяти этого процесса были перенесены на диск. По крайней мере, они больше не находятся в кеше L1 или L2. В такой ситуации нет смысла выгружать приложение, которое выгружает все эти данные и кодовые страницы обратно в память, чтобы «высвободить» ресурсы, которые будут освобождены операционной системой в любом случае, когда процесс завершится. Это относится к управляемым и даже определенным неуправляемым ресурсам. Только ресурсы, которые поддерживают не фоновые потоки, должны быть удалены, иначе процесс останется живым.

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

.NET Framework использует IDisposableинтерфейс как сигнал, даже предупреждая разработчиков, что этот класс должен быть удален. Я не могу думать о каких-либо типах в рамках, которые реализуют IDisposable(исключая явные реализации интерфейса), где удаление является необязательным.

11
12.02.2009 09:34:24
Вызов Dispose совершенно действителен, законен и приветствуется. Объекты, которые реализуют IDisposable, обычно делают это по определенной причине. Единственный раз, когда на показатели производительности GC влияют, это когда вызывается GC.Collect ().
Scott Dorman 11.02.2009 21:17:16
Для многих классов .net удаление является «несколько» необязательным, а это означает, что отказ от экземпляров «обычно» не вызовет никаких проблем, если не сходить с ума, создавая новые экземпляры и оставляя их. Например, код, сгенерированный компилятором для элементов управления, кажется, создает шрифты при создании экземпляров элементов управления и оставляет их при удалении форм; если создать и использовать тысячи элементов управления, это может связать тысячи дескрипторов GDI, но в большинстве случаев элементы управления не создаются и не разрушаются так сильно. Тем не менее, все же следует стараться избегать такого отказа.
supercat 2.08.2011 15:14:49
Я подозреваю, что в случае со шрифтами проблема заключается в том, что Microsoft так и не определила, какой объект отвечает за удаление объекта «шрифт», назначенного элементу управления; в некоторых случаях элементы управления могут совместно использовать шрифт с более долгоживущим объектом, поэтому использование элемента управления Dispose для шрифта было бы плохо. В других случаях шрифт будет назначен элементу управления, и нигде больше, поэтому, если элемент управления не располагает его, никто не будет. Между прочим, этой проблемы со шрифтами можно было бы избежать, если бы существовал отдельный одноразовый класс FontTemplate, поскольку элементы управления, похоже, не используют дескриптор GDI своего шрифта.
supercat 2.08.2011 15:23:50
По теме необязательных Dispose()вызовов см .: stackoverflow.com/questions/913228/…
RJ Cuthbertson 23.06.2015 16:20:02

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

Вы можете взглянуть на эту статью для получения более подробной информации о том, как реализовать шаблон Dispose, но в основном это выглядит так:

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources.
                if (handle != null)
                {
                    handle.Dispose();
                }
            }

            // Dispose unmanaged managed resources.

            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Наиболее важным здесь является метод Dispose (bool), который на самом деле работает в двух разных обстоятельствах:

  • распоряжение == true: метод был вызван прямо или косвенно кодом пользователя. Управляемые и неуправляемые ресурсы могут быть утилизированы.
  • пинг == ложь: метод был вызван средой выполнения из финализатора, и вы не должны ссылаться на другие объекты. Только неуправляемые ресурсы могут быть утилизированы.

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

Весь смысл IDisposable и шаблона dispose не заключается в немедленном освобождении памяти. Единственный раз, когда вызов Dispose даже может сразу же освободить память, это когда он обрабатывает сценарий == false и манипулирует неуправляемыми ресурсами. Для управляемого кода память фактически не будет возвращена до тех пор, пока GC не выполнит цикл сбора, который вы действительно не можете контролировать (кроме вызова GC.Collect (), о котором я уже упоминал, не очень хорошая идея).

Ваш сценарий на самом деле недопустим, так как строки в .NET не используют никаких неуправляемых ресурсов и не реализуют IDisposable, нет способа заставить их «очиститься».

43
11.02.2009 21:23:14
Разве вы не забыли реализовать финализатор?
Budda 2.07.2011 05:19:57
@Budda: Нет, он использует SafeHandle. Нет необходимости в деструкторе.
Henk Holterman 2.11.2011 11:16:28
+1 за добавление сети безопасности для нескольких вызовов в Dispose (). В спецификации сказано, что несколько звонков должны быть безопасными. Слишком много классов Microsoft не могут реализовать это, и вы получаете раздражающее исключение ObjectDisposedException.
Jesse Chisholm 30.08.2012 23:34:56
Но Dispose (bool dispose) - это ваш собственный метод в классе SimpleCleanup, который никогда не будет вызываться фреймворком. Поскольку вы называете его только как «true» в качестве параметра, «утилизация» никогда не будет ложной. Ваш код очень похож на пример MSDN для IDisposable, но в нем отсутствует финализатор, как указывал @Budda, и именно отсюда поступит вызов с утилизацией = false.
yoyo 13.05.2015 22:11:09

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

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

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

2
11.02.2009 18:45:58
Это частные поля, поэтому я думаю, что было бы справедливо предположить, что OP не дает ссылки на них.
mqp 11.02.2009 21:20:39
1) фрагмент кода является просто примером кода, поэтому я просто указываю, что может быть побочный эффект, который легко пропустить; 2) закрытые поля часто являются целью свойства / метода получателя - может быть, слишком много (некоторые люди считают, что получатель / установщик является своего рода анти-паттерном).
Michael Burr 11.02.2009 22:27:39

Сценарии, которые я использую IDisposable: очистить неуправляемые ресурсы, отписаться о событиях, закрыть соединения

Идиома, которую я использую для реализации IDisposable ( не потокобезопасен ):

class MyClass : IDisposable {
    // ...

    #region IDisposable Members and Helpers
    private bool disposed = false;

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing) {
        if (!this.disposed) {
            if (disposing) {
                // cleanup code goes here
            }
            disposed = true;
        }
    }

    ~MyClass() {
        Dispose(false);
    }
    #endregion
}
16
18.10.2013 11:33:20
Полное описание шаблонов можно найти по адресу msdn.microsoft.com/en-us/library/b1yfkh5e.aspx
LicenseQ 11.02.2009 20:41:23
Не следует включать финализатор, если у вас нет неуправляемых ресурсов. Даже в этом случае предпочтительной реализацией является перенос неуправляемого ресурса в SafeHandle.
Dave Black 17.02.2017 19:20:32

После вызова Dispose не должно быть никаких дальнейших вызовов методов объекта (хотя объект должен допускать дальнейшие вызовы Dispose). Поэтому пример в вопросе глупый. Если вызывается Dispose, то сам объект может быть отброшен. Таким образом, пользователь должен просто отбросить все ссылки на весь этот объект (установить для него значение null), и все связанные с ним внутренние объекты будут автоматически очищены.

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

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

Но - и это ключ - они могут быть любой подходящей парой функций. Один создает государство, другой разрушает его. Если состояние было построено, но еще не снесено, то существует экземпляр ресурса. Вы должны принять меры к тому, чтобы демонтаж произошел в нужное время - ресурс не управляется CLR. Единственный автоматически управляемый тип ресурса - это память. Есть два вида: GC и стек. Типы значений управляются стеком (или путём перехода внутри ссылочных типов), а ссылочные типы управляются GC.

Эти функции могут вызывать изменения состояния, которые могут свободно чередоваться, или могут нуждаться в идеальном вложении. Изменения состояния могут быть потокобезопасными или нет.

Посмотрите на пример в вопросе правосудия. Изменения в отступе файла журнала должны быть идеально вложены, иначе все пойдет не так. Также они вряд ли будут безопасны.

Можно собрать мусора, чтобы очистить неуправляемые ресурсы. Но только если функции изменения состояния являются поточно-ориентированными и два состояния могут иметь время жизни, которое перекрывается любым образом. Таким образом, в примере с ресурсом правосудия НЕ должно быть финализатора! Это просто никому не поможет.

Для такого рода ресурсов вы можете просто реализовать IDisposableбез финализатора. Финализатор абсолютно необязателен - так и должно быть. Это скрыто или даже не упоминается во многих книгах.

Затем вы должны использовать это usingутверждение, чтобы иметь возможность убедиться, что оно Disposeвызывается. По сути, это похоже на соединение со стеком (так как финализатор для GC, usingэто для стека).

Недостающая часть заключается в том, что вы должны вручную написать Dispose и вызвать его для ваших полей и вашего базового класса. Программисты C ++ / CLI не должны этого делать. Компилятор пишет это для них в большинстве случаев.

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

Вместо того, чтобы писать класс, вы пишете функцию. Функция принимает делегата для обратного вызова:

public static void Indented(this Log log, Action action)
{
    log.Indent();
    try
    {
        action();
    }
    finally
    {
        log.Outdent();
    }
}

И тогда простой пример будет:

Log.Write("Message at the top");
Log.Indented(() =>
{
    Log.Write("And this is indented");

    Log.Indented(() =>
    {
        Log.Write("This is even more indented");
    });
});
Log.Write("Back at the outermost level again");

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

Этот метод менее полезен, если ресурс того типа, который может иметь перекрывающиеся времена жизни, потому что тогда вы хотите иметь возможность построить ресурс A, затем ресурс B, затем убить ресурс A, а затем убить ресурс B. Вы не можете сделать это если вы заставили пользователя идеально вложить это. Но тогда вам нужно использовать IDisposable(но все же без финализатора, если вы не реализовали безопасность потоков, что не является бесплатным).

19
23.02.2012 10:01:11
re: «После вызова метода Dispose не должно быть никаких дальнейших вызовов методов объекта». «Должен» быть оперативным словом. Если у вас ожидают асинхронные действия, они могут появиться после удаления вашего объекта. Вызывает исключение ObjectDisposedException.
Jesse Chisholm 30.08.2012 23:37:35
Ваш, похоже, единственный ответ, кроме моего, который касается идеи, что неуправляемые ресурсы инкапсулируют состояние, которое GC не понимает. Однако ключевым аспектом неуправляемого ресурса является то, что один или несколько объектов, состояние которых может нуждаться в очистке своего состояния, могут продолжать существовать, даже если объект, который «владеет» ресурсом, не существует. Как тебе мое определение? Довольно похоже, но я думаю, что это делает «ресурс» немного более существительным (это «согласие» внешнего объекта изменить свое поведение в обмен на уведомление о том, когда его услуги больше не нужны)
supercat 26.04.2013 15:43:23
@supercat - если вам интересно, я написал следующее сообщение через пару дней после того, как написал ответ выше: smellegantcode.wordpress.com/2009/02/13/…
Daniel Earwicker 26.04.2013 20:00:34
@DanielEarwicker: интересная статья, хотя я могу вспомнить, по крайней мере, один тип неуправляемых ресурсов, которые вы на самом деле не освещаете: подписку на события из долгоживущих объектов. Подписки на события являются взаимозаменяемыми, но даже если бы память была неограниченной, отказ от их использования может быть дорогостоящим. Например, перечислителю для коллекции, которая допускает изменение во время перечисления, может потребоваться подписка на обновления уведомлений из коллекции, и коллекция может обновляться много раз за время своего существования. Если
supercat 26.04.2013 21:12:09
Пара операций enterи exitявляется основой того, как я думаю о ресурсе. Подписка / отказ от подписки на события должны вписываться в это без труда. С точки зрения ортогональных / взаимозаменяемых характеристик он практически неотличим от утечки памяти. (Это неудивительно, поскольку подписка просто добавляет объекты в список.)
Daniel Earwicker 26.04.2013 21:39:30

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

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

4
11.02.2009 20:32:25

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


Принципы разработки структуры - это руководящие принципы, а не правила. Они говорят вам, для чего прежде всего интерфейс, когда его использовать, как его использовать, а когда нет.

Однажды я прочитал код, который был простым RollBack () при ошибке с использованием IDisposable. Класс MiniTx, приведенный ниже, будет проверять флаг Dispose () и, если Commitвызов не произойдет, он будет вызывать Rollbackсам себя. Это добавило слой косвенности, что значительно облегчило понимание и сопровождение вызывающего кода. Результат выглядел примерно так:

using( MiniTx tx = new MiniTx() )
{
    // code that might not work.

    tx.Commit();
} 

Я также видел, что тайминг / логирование кода делают то же самое. В этом случае метод Dispose () остановил таймер и зарегистрировал, что блок вышел.

using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{
    // code to time...
}

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

7
11.02.2009 21:07:01
Взгляните на пример @Daniel Earwicker с использованием функций более высокого порядка. Для бенчмаркинга, хронометража, логирования и т. Д. Это кажется намного проще.
Aluan Haddad 22.09.2016 03:15:41

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

Открытый класс LargeStuff
  Реализует IDisposable
  Private _Large as string ()

  «Какой-то странный код, который означает, что _Large теперь содержит несколько миллионов длинных строк.

  Public Sub Dispose () Реализует IDisposable.Dispose
    _Large = Nothing
  End Sub

Я понимаю, что реализация Disposable не соответствует текущим рекомендациям, но, надеюсь, вы все поймете эту идею.
Теперь, когда вызывается Dispose, сколько памяти освобождается?

Ответ: нет.
Вызов Dispose может освободить неуправляемые ресурсы, он НЕ МОЖЕТ вернуть управляемую память, это может сделать только GC. Это не значит, что вышеизложенное не является хорошей идеей, следуя вышеприведенному шаблону, на самом деле все еще хорошая идея. После запуска Dispose ничто не мешает GC повторно запросить память, которая использовалась _Large, даже если экземпляр LargeStuff все еще находится в области видимости. Строки в _Large также могут относиться к поколению 0, но экземпляр LargeStuff может относиться к поколению 2, поэтому снова память будет востребована раньше.
Однако нет смысла добавлять финализатор для вызова метода Dispose, показанного выше. Это просто ЗАДЕРЖИТ повторное требование памяти, чтобы позволить финализатору работать.

6
11.02.2009 21:08:56
Если экземпляр LargeStuffбыл достаточно длинным, чтобы попасть в Поколение 2, и если он _Largeсодержит ссылку на вновь созданную строку, которая находится в Поколении 0, то если экземпляр этого объекта LargeStuffбыл оставлен без обнуления _Large, тогда строка, на которую ссылается _Largeбудет храниться до следующей коллекции Gen2. Обнуление _Largeможет позволить удалить строку в следующей коллекции Gen0. В большинстве случаев обнуление ссылок бесполезно, но в некоторых случаях это может принести некоторую пользу.
supercat 9.05.2013 23:10:42

IDisposable хорошо для отписки от событий.

2
22.11.2012 15:16:35

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

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

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

2
22.02.2012 06:38:32
Ну, ИМО, определение неуправляемого объекта понятно; любой не-GC объект .
Eonil 26.05.2014 21:30:46
@Eonil: неуправляемый объект! = Неуправляемый ресурс. Такие вещи, как события, могут быть полностью реализованы с использованием управляемых объектов, но все же представляют собой неуправляемые ресурсы, потому что - по крайней мере в случае недолговечных объектов, подписывающихся на события долгоживущих объектов - GC ничего не знает о том, как их очистить ,
supercat 27.05.2014 13:35:34

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

Видеть:

7
3.06.2013 21:07:50

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

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

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

5
15.06.2013 13:56:16

Первое определение. Для меня неуправляемый ресурс означает некоторый класс, который реализует интерфейс 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
31.08.2015 12:18:20
«Для меня неуправляемый ресурс означает некоторый класс, который реализует интерфейс IDisposable или что-то созданное с использованием вызовов в dll». То есть вы говорите, что любой тип, который is IDisposableсам по себе следует считать неуправляемым ресурсом? Это не кажется правильным. Кроме того, если тип внедрения является чистым типом значения, вы, вероятно, предполагаете, что его не нужно удалять. Это также кажется неправильным.
Aluan Haddad 22.09.2016 03:08:53
Каждый судит сам. Я не люблю добавлять в мой код что-то просто ради дополнения. Это означает, что если я добавлю IDisposable, это означает, что я создал какую-то функциональность, которой GC не может управлять, или я полагаю, что он не сможет правильно управлять своим временем жизни.
Yuriy Zaletskyy 20.04.2018 22:22:47

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

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

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

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

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

1
14.09.2016 05:40:49
По большей части GC не работает, идентифицируя мертвые объекты, а скорее идентифицируя живые. После того, как каждый цикл gc для каждого объекта, который был зарегистрирован для финализации, сохранен в куче больших объектов или является целью динамического объекта WeakReference, система проверит флаг, который указывает, что в последнем цикле GC была найдена активная корневая ссылка и либо добавит объект в очередь объектов, нуждающихся в немедленной финализации, либо освободит объект из кучи больших объектов, либо сделает недействительной слабую ссылку. Циркулярные ссылки не будут поддерживать жизнь объектов, если другие ссылки не существуют.
supercat 15.09.2016 02:39:03

Данный пример кода не является хорошим примером для 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
26.05.2017 05:41:42

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

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

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

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

0
3.10.2018 17:32:35
Я не понял, почему кто-то поставил -1 в твоем ответе
Sebastian Oscar Lopez 23.08.2019 13:12:02