IDisposable, финализаторы и определение неуправляемого ресурса

Я пытаюсь убедиться, что мое пониманиеIDisposable правильно, и в этом я все еще не совсем уверен.

IDisposable кажется, служит двум целям.

To provide a convention to "shut down" a managed object on demand. To provide a convention to free "unmanaged resources" held by a managed object.

Моя путаница связана с определением того, какие сценарии содержат «неуправляемые ресурсы». в игре.

Допустим, вы используете поставляемую MicrosoftIDisposable- реализуемый (управляемый) класс (скажем, связанный с базой данных или сокетом).

How do you know whether it is implementing IDisposable for just 1 or 1&2 above? Are you responsible for making sure that unmanaged resources it may or may not hold internally are freed? Should you be adding a finalizer (would that be the right mechanism?) to your own class that calls instanceOfMsSuppliedClass.Dispose()?
 xyz19 июн. 2009 г., 19:46
Спасибо всем за ответы.

Ответы на вопрос(8)

Здесь не хватает одного финализатора - моя привычка была, если я реализую IDisposable, у меня также есть финализатор для вызова Dispose () на тот случай, если мой клиент этого не сделает. Да, это добавляет накладные расходы, но если вызов Dispose () IS, то вызов GC.SuppressFinalize (this) устраняет его.

 19 июн. 2009 г., 19:24
MSDN довольно явно в шаблоне IDisposable, чтобы включить финализатор. Я не думаю, что "почти наверняка" достаточно хорош, поэтому я думаю, что финализатор с IDisposable важен.
 xyz19 июн. 2009 г., 19:49
Это обсуждение, к которому пытался прийти мой неловко сформулированный вопрос. Различие между классами IDisposable, которые на самом деле содержат неуправляемые ресурсы, и классами IDisposable, которые просто имеют другие экземпляры IDisposable в качестве членов.
 19 июн. 2009 г., 21:18
Называй меня глупым, но я просто не вижу разницы. Если я напишу класс, который использует одноразовые объекты, я собираюсь реализовать IDisposable с финализатором, чтобы убедиться, что он очищен. Я не думаю, что должен думать о том, действительно ли объекты, которые я использую, действительно распоряжаться чем-либо или нет, точно так же, как МОИ вызывающие абоненты не должны принимать такое решение относительно моего класса. IDisposable - это контракт, и его значение понятно.
 08 мая 2010 г., 10:50
@ n8wrl: ну, к сожалению, ты должен думать об этом. Вы не должны вызывать другие классы & apos; Утилизировать при утилизации через финализатор. Посмотрите на скороговорку MScarefully - если вы реализуете шаблон правильно, то ваш класс НИЧЕГО не сделает, когда Dispose будет вызван из финализатора, из-за этого "if (утилизировать)" quot; вещь.
 19 июн. 2009 г., 15:21
Что делает ваш Dispose? Если все, что вы делаете, это вызываете Dispose для других объектов, то добавление собственного финализатора - пустая трата усилий и времени выполнения. Объекты, которые фактически удерживают неуправляемые ресурсы (сокеты, дескрипторы файлов, дескрипторы GDI +, дескрипторы окон и т. Д.), Почти наверняка уже будут иметь финализаторы и будут очищаться, если они GCed без предварительной утилизации. Вам не нужно добавлять дополнительные издержки (и сложность кода), чтобы сделать то, что произошло бы в любом случае.

Почему это должно иметь значение для вас?

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

По причине 1 или 2 не обязательно.

Если рассматриваемый класс поставляется Microsoft (т. Е. Базой данных и т. Д.), То обработка Dispose (из IDisposable), скорее всего, уже будет предпринята, и вам остается только позвонить. Например, стандартная практика использования базы данных будет выглядеть так:

//...
using (IDataReader dataRead = new DataReaderObject())
{
   //Call database
}

По сути это то же самое, что и запись:

IDataReader dataRead = null;
try
{
    dataRead = new DataReaderObject()
    //Call database
}
finally
{
    if(dataRead != null)
    {
        dataRead.Dispose();
    }
}

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

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

 19 июн. 2009 г., 15:23
Все еще не компилируется; см. мои заметки об ответе ВВС. Вы должны начать работу непосредственно перед началом & quot; try & quot; блок. Или используйте & quot; используя & quot ;.
 19 июн. 2009 г., 16:06
Смысл кода - это обобщенное описание того, что делает оператор using. Код не предназначен для использования, главным образом потому, что использование является принятым методом. Но вы правы, код не скомпилируется, объект dataRead должен быть установлен как NULL до его инициализации. Хотя есть причина для инициализации объекта внутри оператора try. Оператор using обернет объект в попытку, потому что объект может иметь сложную инициализацию, которая вызывает исключение. Оператор using справится с ними и завершится передачей в finally.
 19 июн. 2009 г., 15:09
Что ж, я тоже не правильно определил область своего объекта IDataReader, это было как бы краткое описание "использования" & quot; заявление. Я буду редактировать свой пост, чтобы не вводить людей в заблуждение или, что еще хуже, рискнуть заставить их написать что-то таким образом, спасибо!
Решение Вопроса
How do you know whether it is implementing IDisposable for just 1 or 1&2 above?

Ответ на ваш первый вопрос - «вам не нужно знать». Если вы используете сторонний код, то в какой-то степени вы его милосердны - вы должны верить, что он правильно распоряжается собой, когда вы вызываете Dispose для него. Если вы не уверены или считаете, что это ошибка, вы всегда можете попробовать использовать Reflector (), чтобы разобрать его (если это возможно) и проверить, что он делает.

Am I responsible for making sure that unmanaged resources it may or may not hold internally are freed? Should I be adding a finalizer (would that be the right mechanism?) to my own class that calls instanceOfMsSuppliedClass.Dispose()?

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

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

 08 мая 2010 г., 10:54
Связанная статья, призывающаяDispose синтаксис деструктора ... не знаю ... Я предпочитаю этот:nitoprograms.blogspot.com/2009/08/… и грязные детали вcodeproject.com/KB/dotnet/idisposable.aspx
 19 июн. 2009 г., 20:08
Правильный! И как примечание: если вам когда-либо приходилось писать финализатор, вы должны завершать только те объекты, которые принадлежат вашему объекту напрямую. Завершение других ресурсов объекта может привести к всевозможным неприятностям.
 xyz19 июн. 2009 г., 19:42
& quot; В конечном счете, вызов Dispose () .. дает пользователю возможность немедленно очистить его ... Но даже если вы забудете, это произойдет в конце концов, когда объект не рутирован. & quot; Это информация, которую я действительно хотел получить. Таким образом, если автор класса, который содержит неуправляемые ресурсы ACTUAL, правильно реализует шаблон IDisposable + Finalizer, в конце концов очистка всегда будет происходить так или иначе.

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

Когда говорят, что объект «содержит неуправляемые ресурсы», в действительности подразумевается, что объект обладает информацией и импульсом, необходимыми для выполнения некоторой необходимой операции очистки на некоторых объектах.other субъекта, и нет особой причины полагать, что информация и стимул существуют где-либо еще. Если единственный объект с такой информацией и стимулом полностью заброшен, требуемая операция очистки никогда не произойдет. Цель .Dispose - заставить объект выполнить любую необходимую очистку, чтобы от него можно было безопасно отказаться.

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

 21 февр. 2011 г., 17:25
@ Джон Сондерс: Если такие отношения не составляют «неуправляемые ресурсы», то каковы они?
 21 февр. 2011 г., 17:11
@John Saunders: Управляемый код - это код, выполняемый в CLR, в котором используются объекты, управляемые сборщиком мусора CLR. Неуправляемый код, также называемый «собственным кодом», - это код, который не выполняется в среде CLR. Поскольку CLR ничего не знает о том, что происходит вне его, дескрипторы файлов, дескрипторы GDI, блоки памяти, выделенные API, и т. Д. Все являются "собственными ресурсами"; Собственные ресурсы также являются «неуправляемыми ресурсами», хотя обратное не всегда верно ...
 20 февр. 2011 г., 22:34
-1: & quot; Неуправляемый ресурс & quot; не является неправильным. Это имеет очень четкое значение, и это не то, о чем вы только что заявили. Разве вы не знаете, что "удалось"? значит в контексте .NET?
 21 февр. 2011 г., 17:24
@John Saunders: Если объект (например, итератор) получает блокировку для другого объекта и затем отменяется, блокировка (и все, к чему он контролирует доступ) может быть непригодна с этой точки вперед. GC сможет очистить вещи, как только издатель события или объект, содержащий блокировку, выйдет из области видимости, но нет никакой гарантии того, когда это произойдет. Ключевой вопрос в обоих случаях состоит в том, что существует важный тип отношений между объектами, о которых CLR не знает, и, таким образом, требуется важная очистка, которую GC не будет выполнять.
 21 февр. 2011 г., 17:17
@ Джон Сондерс: Хотя CLR много знает о том, что происходит под ним, он не знает всех важных отношений, которые могут существовать между объектами. Совершенно возможно - и вряд ли необычно - недолговечный объект вносит изменения в долгоживущий объект, что вызовет проблемы, если они не будут очищены. Два распространенных примера - это подписка на события и блокировки. Если объект с коротким сроком полезного использования подписывается на событие из долгоживущего объекта и впоследствии покидается, этот долгоживущий объект будет бесполезно поддерживаться подписчиком.

Yes, you're responsible to call the Dispose method - or better use using statement. If an object is implementing IDisposable, you should always dispose it, no matter what.

using (var myObj = new Whatever())
{
   // ..
}

похож на

{
  var myObj;
  try
  {
     myObj = new Whatever();
     // ..
  } 
  finally
  {
    if (myObj != null)
    {
      ((IDisposable)myObj).Dispose();
    }
  }
} // object scope ends here

EDIT: Добавлено try / finally благодаря Talljoe - вау, это сложно сделать правильно :)

EDIT2: Я не говорю, что вы должны использовать второй вариант. Я просто хотел показать, что & quot; с использованием & quot; хороший синтаксический сахар для набора кода, который может быть довольно запутанным и трудно понять.

 18 июн. 2009 г., 17:24
@ Talljoe - это верно, многие люди забывают о проверке myObj! = Null.
 18 июн. 2009 г., 17:23
На самом деле, это похоже на: независимо от того, что myObj; try {myObj = новый, что угодно (); / * .. * /} finally {if (myObj! = null) ((IDisposable) myObj) .Dispose;
 18 июн. 2009 г., 19:01
@ Страж: Может быть, а может и нет. И даже если сейчас все в порядке, реализация может измениться в будущих версиях платформы. Если вы гарантируете, что вы всегда располагаете какими-либо объектами IDisposable, вам не нужно беспокоиться об их базовой реализации или любых будущих изменениях в ней.
 18 июн. 2009 г., 17:27
Требуется ли Dispose () ВСЕГДА? Я всегда думал, что в случае с такими объектами, как FileStream, выполнение FileStream.Close () и разрешение сборщику мусора собирать его не приведет к каким-либо проблемам. Утилизация / использование было бы лучше, я бы согласился, но не все ли это будет в порядке?
 19 июн. 2009 г., 15:16
Этот код не будет компилироваться, потому что, когда вы доберетесь до «наконец», myObj может быть неинициализирован. Например, если & quot; новое, что угодно () & quot; бросил исключение, тогда myObj никогда бы не был назначен. Как правило, вы бы поставить назначение простоbefore начало «попробовать» блок. Тогда вам не нужна нулевая проверка. В качестве альтернативы вы можете не изобретать велосипед, а просто использовать & quot; использование & quot; ключевое слово - это то, для чего оно существует.

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

Неуправляемые ресурсы - это ресурсы, которые вы создали бы в (управляемом) C ++, где вы выделяете память с помощью указателей и & quot; new & quot; заявления, а не "gcnew" заявления. Когда вы создаете класс в C ++, вы несете ответственность за удаление этой памяти, поскольку она является собственной памятью или неуправляемой, а сборщик мусора об этом не говорит. Вы также можете создать эту неуправляемую память посредством распределений маршала и, я полагаю, небезопасного кода.

При использовании Managed C ++ вам не нужно вручную реализовывать класс IDisposable. Когда вы пишете деконструктор, он будет скомпилирован в функцию Dispose ().

Вы должны всегда вызывать Dispose для объектов, которые реализуют IDisposable (если только они специально не сообщают вам, что это полезное соглашение, например HtmlHelper.BeginForm ASP.NET MVC). Вы можете использовать & quot; используя & quot; заявление, чтобы сделать это легко. Если вы держитесь за ссылку на IDisposable в своем классе в качестве поля-члена, тогда вы должны реализовать IDisposable, используяОдноразовый узор очистить тех членов. Если вы запустите инструмент статического анализа, такой как FxCop, он скажет вам то же самое.

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

 18 июн. 2009 г., 17:25
Я всегда использую FXCop, чтобы проверить, что я вызвал Dispose, и почти всегда делаю это с помощью оператора using.

Ваш ответ на вопрос