IDisposable, финализаторы и определение неуправляемого ресурса - PullRequest
5 голосов
/ 18 июня 2009

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

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

  1. Чтобы обеспечить соглашение об "отключении" управляемого объекта по требованию.
  2. Предоставление соглашения об освобождении "неуправляемых ресурсов", находящихся в управляемом объекте.

Моя путаница возникает из-за определения, в каких сценариях присутствуют "неуправляемые ресурсы".

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

  1. Откуда вы знаете, реализует ли он IDisposable только для 1 или 1 & 2 и выше?
  2. Отвечаете ли вы за то, чтобы освободить неуправляемые ресурсы, которые могут или не могут храниться внутри компании? Если вы добавляете финализатор (это будет правильный механизм?) В ваш собственный класс, который вызывает instanceOfMsSuppliedClass.Dispose ()?

Ответы [ 8 ]

10 голосов
/ 18 июня 2009
  1. Как узнать, реализует ли он IDisposable всего за 1 или 1 и 2 выше?

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

  1. Я несу ответственность за то, чтобы неуправляемые ресурсы не держите внутренне освобождены? Должен Я добавляю финализатор (это будет правильный механизм?) в моем собственном классе что вызывает instanceOfMsSuppliedClass.Dispose ()

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

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

5 голосов
/ 18 июня 2009

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

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

1 голос
/ 21 февраля 2011

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

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

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

1 голос
/ 18 июня 2009

Если рассматриваемый класс предоставлен 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 самостоятельно, реализация зависит от вас. После того, как вы унаследуете его, вы должны убедиться, что метод содержит код, необходимый для удаления любых соединений с БД, которые вы могли создать вручную, освобождая ресурсы, которые могут остаться, или предотвратите уничтожение объекта, или просто очистив большие пулы ресурсов (например, изображения ). Это также включает неуправляемые ресурсы, например, код, помеченный внутри «небезопасного» блока, по сути является неуправляемым кодом, который может обеспечивать прямое манипулирование памятью, что, безусловно, требует очистки.

1 голос
/ 18 июня 2009

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

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

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

0 голосов
/ 19 июня 2009

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

0 голосов
/ 18 июня 2009

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

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

Нет необходимости по причине 1 или по причине 2.

0 голосов
/ 18 июня 2009

Да, вы обязаны вызывать метод Dispose - или лучше использовать оператор using. Если объект реализует IDisposable, вы всегда должны его утилизировать, несмотря ни на что.

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

похож на

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

РЕДАКТИРОВАТЬ : добавлена ​​попытка / наконец благодаря Talljoe - вау, это сложно сделать правильно:)

РЕДАКТИРОВАТЬ 2: Я не говорю, что вы должны использовать второй вариант. Я просто хотел показать, что «использование» является хорошим синтаксическим сахаром для набора кода, который может быть довольно запутанным и трудно понять.

...