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


Я знаю из чтения документация MSDN что «первичное» использование 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в приведенном выше коде содержалось миллион строк, и вы хотели освободить эту память Теперь , а не ждать сборщика мусора. Выполнил бы этот код?


1358


источник


Ответы:


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

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

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

public void Cleanup()

public void Shutdown()

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

public void Dispose()

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

public interface IDisposable
{
   void Dispose()
}

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

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

И вы сделали. Кроме того, вы можете сделать лучше.


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

Уничтожение нашего объекта сборщиком мусора - это идеально чтобы освободить эти надоедливые неуправляемые ресурсы. Мы делаем это, переопределяя 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 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 указывает на это:

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

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


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

Чтение бонусов

Для тех, кто любит стиль этого ответа (объясняя Зачем , Итак как становится очевидным), я предлагаю вам прочитать главу 1 из «Дон Бокса», «Существенный COM»:

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

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

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

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

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


2264



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.");
        }
    }
}

54



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

Вы можете взглянуть на это статья для получения дополнительной информации о том, как реализовать шаблон 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), который фактически работает в двух разных обстоятельствах:

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

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

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

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


36



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

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

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

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

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

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

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

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

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

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

Существует альтернатива, которую я предпочитаю для состояний, которые идеально подходят и не являются потокобезопасными (кроме всего прочего, избегая IDisposable spares, у вас возникает проблема с аргументом с кем-то, кто не может удержаться от добавления финализатора для каждого класса, который реализует 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(но все равно без финализатора, если только вы не реализовали потоки безопасности, что не является бесплатным).


17



Сценарии Я использую 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
}

14



If MyCollection is going to be garbage collected anyway, then you shouldn't need to dispose it. Doing so will just churn the CPU more than necessary, and may even invalidate some pre-calculated analysis that the garbage collector has already performed.

I use IDisposable to do things like ensure threads are disposed correctly, along with unmanaged resources.

EDIT In response to Scott's comment:

The only time the GC performance metrics are affected is when a call the [sic] GC.Collect() is made"

Conceptually, the GC maintains a view of the object reference graph, and all references to it from the stack frames of threads. This heap can be quite large and span many pages of memory. As an optimisation, the GC caches its analysis of pages that are unlikely to change very often to avoid rescanning the page unnecessarily. The GC receives notification from the kernel when data in a page changes, so it knows that the page is dirty and requires a rescan. If the collection is in Gen0 then it's likely that other things in the page are changing too, but this is less likely in Gen1 and Gen2. Anecdotally, these hooks were not available in Mac OS X for the team who ported the GC to Mac in order to get the Silverlight plug-in working on that platform.

Another point against unnecessary disposal of resources: imagine a situation where a process is unloading. Imagine also that the process has been running for some time. Chances are that many of that process's memory pages have been swapped to disk. At the very least they're no longer in L1 or L2 cache. In such a situation there is no point for an application that's unloading to swap all those data and code pages back into memory to 'release' resources that are going to be released by the operating system anyway when the process terminates. This applies to managed and even certain unmanaged resources. Only resources that keep non-background threads alive must be disposed, otherwise the process will remain alive.

Now, during normal execution there are ephemeral resources that must be cleaned up correctly (as @fezmonkey points out database connections, sockets, window handles) to avoid unmanaged memory leaks. These are the kinds of things that have to be disposed. If you create some class that owns a thread (and by owns I mean that it created it and therefore is responsible for ensuring it stops, at least by my coding style), then that class most likely must implement IDisposable and tear down the thread during Dispose.

The .NET framework uses the IDisposable interface as a signal, even warning, to developers that the this class must be disposed. I can't think of any types in the framework that implement IDisposable (excluding explicit interface implementations) where disposal is optional.


11



Yep, that code is completely redundant and unnecessary and it doesn't make the garbage collector do anything it wouldn't otherwise do (once an instance of MyCollection goes out of scope, that is.) Especially the .Clear() calls.

Answer to your edit: Sort of. If I do this:

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

It's functionally identical to this for purposes of memory management:

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

If you really really really need to free the memory this very instant, call GC.Collect(). There's no reason to do this here, though. The memory will be freed when it's needed.


10



If you want to delete right now, use unmanaged memory.

See:


7



I won't repeat the usual stuff about Using or freeing un-managed resources, that has all been covered. But I would like to point out what seems a common misconception.
Given the following code

Public Class LargeStuff
  Implements IDisposable
  Private _Large as string()

  'Some strange code that means _Large now contains several million long strings.

  Public Sub Dispose() Implements IDisposable.Dispose
    _Large=Nothing
  End Sub

I realise that the Disposable implementation does not follow current guidelines, but hopefully you all get the idea.
Now, when Dispose is called, how much memory gets freed?

Answer: None.
Calling Dispose can release unmanaged resources, it CANNOT reclaim managed memory, only the GC can do that. Thats not to say that the above isn't a good idea, following the above pattern is still a good idea in fact. Once Dispose has been run, there is nothing stopping the GC re-claiming the memory that was being used by _Large, even though the instance of LargeStuff may still be in scope. The strings in _Large may also be in gen 0 but the instance of LargeStuff might be gen 2, so again, memory would be re-claimed sooner.
There is no point in adding a finaliser to call the Dispose method shown above though. That will just DELAY the re-claiming of memory to allow the finaliser to run.


6



In the example you posted, it still doesn't "free the memory now". All memory is garbage collected, but it may allow the memory to be collected in an earlier generation. You'd have to run some tests to be sure.


The Framework Design Guidelines are guidelines, and not rules. They tell you what the interface is primarily for, when to use it, how to use it, and when not to use it.

I once read code that was a simple RollBack() on failure utilizing IDisposable. The MiniTx class below would check a flag on Dispose() and if the Commit call never happened it would then call Rollback on itself. It added a layer of indirection making the calling code a lot easier to understand and maintain. The result looked something like:

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

    tx.Commit();
} 

I've also seen timing / logging code do the same thing. In this case the Dispose() method stopped the timer and logged that the block had exited.

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

So here are a couple of concrete examples that don't do any unmanaged resource cleanup, but do successfully used IDisposable to create cleaner code.


5