Объекты никогда не выходят из области видимости в C #, как в C ++. Они обрабатываются сборщиком мусора автоматически, когда они больше не используются. Это более сложный подход, чем C ++, где область действия переменной полностью детерминирована. Сборщик мусора CLR активно просматривает все созданные объекты и работает, если они используются.
Объект может выйти «из области видимости» в одной функции, но если его значение будет возвращено, то GC посмотрит, удерживает ли вызывающая функция возвращаемое значение.
Установка ссылок на объекты не null
является необходимой, поскольку сборка мусора работает, определяя, на какие объекты ссылаются другие объекты.
На практике вам не нужно беспокоиться о разрушении, оно просто работает и это здорово :)
Dispose
должен вызываться на всех объектах, которые реализуются, IDisposable
когда вы закончите работать с ними. Обычно вы используете using
блок с этими объектами, например:
using (var ms = new MemoryStream()) {
//...
}
РЕДАКТИРОВАТЬ На переменной области. Крейг спросил, влияет ли переменная область действия на время жизни объекта. Чтобы правильно объяснить этот аспект CLR, мне нужно объяснить несколько понятий из C ++ и C #.
Фактическая область видимости переменной
В обоих языках переменную можно использовать только в той же области, в которой она была определена - класс, функция или блок операторов, заключенный в фигурные скобки. Тонкое отличие, однако, состоит в том, что в C # переменные не могут быть переопределены во вложенном блоке.
В C ++ это совершенно законно:
int iVal = 8;
//iVal == 8
if (iVal == 8){
int iVal = 5;
//iVal == 5
}
//iVal == 8
Однако в C # вы получаете ошибку компилятора:
int iVal = 8;
if(iVal == 8) {
int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else
}
Это имеет смысл, если вы посмотрите на сгенерированный MSIL - все переменные, используемые функцией, определены в начале функции. Посмотрите на эту функцию:
public static void Scope() {
int iVal = 8;
if(iVal == 8) {
int iVal2 = 5;
}
}
Ниже сгенерированный IL. Обратите внимание, что iVal2, который определен внутри блока if, фактически определен на уровне функций. Фактически это означает, что C # имеет только область действия класса и уровня функций, что касается времени жизни переменной.
.method public hidebysig static void Scope() cil managed
{
// Code size 19 (0x13)
.maxstack 2
.locals init ([0] int32 iVal,
[1] int32 iVal2,
[2] bool CS$4$0000)
//Function IL - omitted
} // end of method Test2::Scope
Область действия C ++ и время жизни объекта
Всякий раз, когда переменная C ++, размещенная в стеке, выходит из области видимости, она разрушается. Помните, что в C ++ вы можете создавать объекты в стеке или в куче. Когда вы создаете их в стеке, когда выполнение выходит из области видимости, они выталкиваются из стека и уничтожаются.
if (true) {
MyClass stackObj; //created on the stack
MyClass heapObj = new MyClass(); //created on the heap
obj.doSomething();
} //<-- stackObj is destroyed
//heapObj still lives
Когда объекты C ++ создаются в куче, они должны быть явно уничтожены, иначе это утечка памяти. Нет такой проблемы с переменными стека, хотя.
Время жизни объекта C #
В CLR объекты (т.е. ссылочные типы) всегда создаются в управляемой куче. Это дополнительно подкрепляется синтаксисом создания объекта. Рассмотрим этот фрагмент кода.
MyClass stackObj;
В C ++ это создаст экземпляр MyClass
в стеке и вызовет его конструктор по умолчанию. В C # это создаст ссылку на класс MyClass
, который ни на что не указывает. Единственный способ создать экземпляр класса - использовать new
оператор:
MyClass stackObj = new MyClass();
В некотором смысле, объекты C # во многом похожи на объекты, которые создаются с использованием new
синтаксиса в C ++ - они создаются в куче, но в отличие от объектов C ++, они управляются средой выполнения, поэтому вам не нужно беспокоиться об их уничтожении.
Поскольку объекты всегда находятся в куче, тот факт, что ссылки на объекты (т. Е. Указатели) выходят из области видимости, становится спорным. При определении необходимости сбора объекта требуется больше факторов, чем просто наличие ссылок на объект.
C # объект ссылки
Джон Скит сравнил ссылки на объекты в Java с кусочками строк, которые прикреплены к всплывающей подсказке, которая является объектом. Та же аналогия применима к ссылкам на объекты C #. Они просто указывают на местоположение кучи, содержащей объект. Таким образом, установка его в null не оказывает непосредственного влияния на время жизни объекта, баллон продолжает существовать до тех пор, пока GC не «вытолкнет» его.
Продолжая аналогию с воздушным шаром, представляется логичным, что, если к воздушному шару не прикреплены нити, его можно уничтожить. Фактически именно так работают объекты с подсчетом ссылок в неуправляемых языках. За исключением того, что этот подход не работает для циклических ссылок очень хорошо. Представьте себе два воздушных шарика, которые соединены друг с другом цепочкой, но ни у одного из них нет цепочки ни к чему другому. По простым правилам подсчета ссылок они оба продолжают существовать, даже если вся группа воздушных шаров «осиротела».
.NET объекты очень похожи на гелиевые шарики под крышей. Когда крыша открывается (GC бежит) - неиспользованные воздушные шары всплывают, хотя могут быть группы воздушных шаров, которые связаны друг с другом.
.NET GC использует комбинацию поколений GC и меток и разверток. Подход с использованием поколений включает в себя среду выполнения, предпочитающую проверять объекты, которые были выделены в последнее время, поскольку они с большей вероятностью будут неиспользованными, а разметка и развертка - во время выполнения, просматривая весь граф объектов и выясняя, есть ли группы объектов, которые не используются. Это адекватно решает проблему круговой зависимости.
Кроме того, .NET GC работает в другом потоке (так называемом потоке финализатора), так как у него есть немало дел, и выполнение этого в основном потоке прерывает вашу программу.