2009-09-26 2 views
8

Могу ли я доверять тому, что объект уничтожен, а его деструктор вызывается немедленно, когда он выходит из области видимости на C#?C# - Объекты немедленно уничтожаются при выходе из сферы действия?

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

Спасибо.

+0

@RA - извините за задержку, имели некоторые Занятия IRL делать :) Добавлены некоторые примеры сейчас, надеюсь, что это еще раз пояснит. –

ответ

24

Нет, .Net и hense C# полагаются на управление памятью сбора мусора. Поэтому деструкторы (которые в .Net называются финализаторами) не вызываются до тех пор, пока GC не найдет нужным уничтожить объекты.

Дополнительно: большинство «обычных» объектов в C# не имеют деструкторов. Если вам нужен шаблон деструктора, вы должны реализовать IDisposable interface с Dispose Pattern. На одноразовых объектах вы также должны убедиться, что метод Dispose вызывается, либо с помощью using keyword, либо напрямую вызывает метод.

Для дальнейшего (надеюсь) уточнения: детерминированное удаление полезно в .Net, например. когда вам нужно явно освобождать ресурсы, которые не управляются средой .Net. Примерами таких ресурсов являются файловые дескрипторы, соединения с базами данных и т. Д. Обычно важно, чтобы эти ресурсы были освобождены, как только они больше не нужны. Таким образом, мы не можем дождаться, когда GC их освободит.

Для того чтобы получить детерминированное удаление (аналогично поведению C++) в недетерминированном мире .Net GC, классы .Net полагаются на интерфейс IDisposable. Заимствование из Dispose Pattern, вот несколько примеров:

Во-первых, инстанцировании располагаемый ресурс и затем позволяя объект выходить из области видимости, оставит его до GC для ликвидации объекта:

1. { 
2.  var dr = new DisposableResource(); 
3. } 

Для исправить это мы можем прямо распоряжаться объектом:

1. { 
2.  var dr = new DisposableResource(); 
3. 
4.  ... 
5. 
6.  dr.Dispose(); 
7. } 

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

1. var dr = new DisposableResource(); 
2. try 
3. { 
4.  ... 
5. } 
6. finally 
7. { 
8.  dr.Dispose(); 
9. } 

Поскольку эта модель часто требуется, C# включает в себя, используя ключевое слово, чтобы упростить вещи. Следующий пример эквивалентен вышесказанному:

1. using (var dr = new DisposableResource()) 
2. { 
3.  ... 
4. } 
+1

Вопрос не знает тонкостей того, что такое деструктор C#, поэтому я не думаю, что вы должны просто назвать деструктор финализатором и сделать с ним. Деструкторы C++ и деструкторы C# - это очень разные вещи. * – Joren

+1

Также имейте в виду, что сборка .NET Garbage Collection основана на «поколении», поэтому объект может потенциально быть мусором, собранным коллекцией Generation 0, но не был полностью «разрушен» "или" удалены "из памяти. Также подумайте о таких вещах, как «Слабые ссылки», посредством которых объект может быть собран из мусора, но все еще доступен по коду вашего приложения! В принципе, объект будет продолжаться до тех пор, пока он хочет выполнения. Вы не имеете права говорить по этому поводу! ;) (http://www.simple-talk.com/dotnet/.net-framework/understanding-garbage-collection-in-.net/) – CraigTP

+0

@Peter: Спасибо за ваш ответ. Вы предполагаете, что IDisposable предлагает способ достижения поведения области видимости, например, на C++? Не могли бы вы рассказать о нем и, возможно, добавить код? – sharkin

0

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

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

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

4

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

К счастью, нет никакой реальной причины, почему вы хотели бы это гарантировать. Если вам нужна память, тогда GC восстановит ее. Если вы этого не сделаете, зачем заботиться, есть ли еще какой-то объект для мусора? Это не утечка памяти: GC все еще может ее найти и очистить в любое время.

4

Нет, это не гарантируется. Подобно языкам, таким как Java, на C# сборщик мусора запускается, когда это необходимо (то есть когда куча становится слишком полной). Однако, когда ваши объекты реализуют IDisposable, i. е. у них есть метод Dispose(), и он должен быть вызван, то вы можете воспользоваться using ключевым словом:

using (var foo = new DisposableObject()) { 
    // do something with that 
} 

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

Примечание: IDisposable встречается во многих типах, в первую очередь GDI +, а также соединениях с базой данных, транзакциях и т. Д., Так что это может быть действительно правильный шаблон здесь.

Примечание 2: За кулисами над блоком будут переводятся в try/finally блока:

var foo = new DisposableObject(); 
try 
{ 
    // do something with that 
} 
finally 
{ 
    foo.Dispose(); 
} 

Но что перевод делается компилятором и очень удобно для не забывая называть Dispose().

0

Нет, если вы обратитесь к спецификации CLI (стр. 8.9.6.7 о Финализаторах) http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf вы можете найти следующие

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

, но оно не должно.

7

Номер Объект на самом деле не «выходит за рамки», ссылка на него (то есть переменная, которую вы используете для доступа к ней).

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

Если ваш объект является ресурсом (например, дескриптор файла, подключение к базе данных), он должен реализовать интерфейс IDisposable (который обязывает объект реализовывать метод Dispose() для очистки любых открытых подключений и т. Д.).Лучшей практикой для вас в этом случае было бы создание объекта как части блока using, так что, когда этот блок будет завершен, ваше приложение автоматически вызовет метод Dispose() объектов, который позаботится о закрытии вашего соединения file/db /без разницы.

например.


using (var conn = new DbConnection()) 
{ 
    // do stuff with conn 
} // conn.Dispose() is automatically called here. 

using блок лишь некоторые синтаксический сахар, который оборачивает ваши взаимодействия с conn объекта в try блоке, наряду с finally блок, который только называет conn.Dispose()

Смежные вопросы