Откуда переменная = null как «уничтожение объекта»? - PullRequest
22 голосов
/ 28 июня 2010

Работая на нескольких унаследованных системах, написанных в различных версиях .NET, во многих разных компаниях, я нахожу примеры следующего шаблона:

public void FooBar()
{
    object foo = null;
    object bar = null;

    try
    {
       foo = new object();
       bar = new object();

       // Code which throws exception.
    }
    finally
    {
       // Destroying objects
       foo = null;
       bar = null;
    }

}

Любому, кто знает, как работает управление памятью.NET, этот вид кода крайне не нужен;сборщику мусора не требуется, чтобы вы вручную назначали null, чтобы сообщить, что старый объект может быть собран, а также назначение null не инструктирует GC немедленно собрать объект.

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

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

Ответы [ 14 ]

22 голосов
/ 28 июня 2010

Это FUD программирование культа груза (благодаря Daniel Earwicker ) разработчиками, которые привыкли к "свободным" ресурсам, плохим реализациям GC и плохим API.

Некоторые сборщики мусора не справились с круговыми ссылками. Чтобы избавиться от них, нужно было разорвать цикл «где-то». Куда? Ну, если сомневаешься, то везде. Делайте это в течение года, и это будет у вас под рукой.

Также установка поля в null дает вам идею «делать что-то», потому что, как разработчики, мы всегда боимся «что-то забыть».

Наконец, у нас есть API, которые должны быть явно закрыты, потому что нет реальной языковой поддержки, чтобы сказать «закройте это, когда я закончу с этим», и дайте компьютеру разобраться, как в GC. Таким образом, у вас есть API, где вы должны вызывать код очистки, и API, где вы этого не делаете. Это отстой и поощряет такие паттерны, как указано выше.

8 голосов
/ 28 июня 2010

Возможно, это было из VB, который использовал стратегию подсчета ссылок для управления памятью и временем жизни объекта.Установка ссылки на Nothing (эквивалентно нулю) уменьшит счетчик ссылок.Как только этот счет стал равным нулю, объект был уничтожен синхронно.Счетчик будет автоматически уменьшаться при выходе из области действия метода, поэтому даже в VB этот метод был в основном бесполезен, однако были особые ситуации, когда вы хотели бы жадно уничтожить объект, как показано следующим кодом.

В приведенном выше коде объект, на который ссылается big, задержался бы до конца без вызова Set big = Nothing.Это может быть нежелательно, если другие вещи в методе были трудоемкой операцией или генерировали больше памяти.

3 голосов
/ 28 июня 2010

Чаще встречается в языках с детерминированной сборкой мусора и без RAII, таких как старый Visual Basic, , но даже там он не нужен и там часто приходилось прерывать циклические ссылки.Так что, возможно, это действительно связано с плохими программистами на C ++, которые повсеместно используют тупые указатели.В C ++ имеет смысл устанавливать нулевые указатели на 0 после их удаления, чтобы предотвратить двойное удаление.

3 голосов
/ 28 июня 2010

Это происходит из C / C ++, где явным образом было установлено, что установка указателей на null была нормой (для устранения висячих указателей )

После звонка бесплатно ():

#include <stdlib.h>
{
    char *dp = malloc ( A_CONST );

    // Now that we're freeing dp, it is a dangling pointer because it's pointing
    // to freed memory
    free ( dp );

    // Set dp to NULL so it is no longer dangling
    dp = NULL;
}

Разработчики классического VB также сделали то же самое при написании своих COM-компонентов, чтобы предотвратить утечки памяти.

2 голосов
/ 28 июня 2010

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

Вы видите этот шаблон и в VB / VBScript все время, по разным причинам. Я размышлял немного о том, что может вызвать это здесь:

http://blogs.msdn.com/b/ericlippert/archive/2004/04/28/122259.aspx

2 голосов
/ 28 июня 2010

Я думаю, что это было распространенным недоразумением среди бывших разработчиков C / C ++.Они знали, что GC освободит их память, но они не понимали, когда и как.Просто почистите его и продолжайте:)

2 голосов
/ 28 июня 2010

Я много видел это в коде VBScript (классический ASP), и я думаю, что это оттуда.

1 голос
/ 28 июня 2010

Рассмотрим небольшую модификацию:

public void FooBar() 
{ 
    object foo = null; 
    object bar = null; 

    try 
    { 
       foo = new object(); 
       bar = new object(); 

       // Code which throws exception. 
    } 
    finally 
    { 
       // Destroying objects 
       foo = null; 
       bar = null; 
    } 
    vaboom(foo,bar);
} 

Автор (ы), возможно, хотел убедиться, что великий Vaboom (*) не получит указатели на уродливые объекты, если исключение было ранее выброшено и перехвачено. Паранойя, приводящая к оборонительному кодированию, не обязательно является плохой вещью в этом бизнесе.

(*) Если вы знаете, кто он, вы знаете.

1 голос
/ 28 июня 2010

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

1 голос
/ 28 июня 2010

Это происходит из C / C ++, где выполнение free () / delete для уже освобожденного указателя может привести к сбою, тогда как освобождение NULL-указателя просто ничего не делает.

Это означает, что эта конструкция (C ++) вызовет проблемы

void foo()
{
  myclass *mc = new myclass(); // lets assume you really need new here
  if (foo == bar)
  {
    delete mc;
  }
  delete mc;
}

пока это будет работать

void foo()
{
  myclass *mc = new myclass(); // lets assume you really need new here
  if (foo == bar)
  {
    delete mc;
    mc = NULL;
  }
  delete mc;
}

Вывод: в C #, Java и практически любом другом языке для сбора мусора ИТ совершенно не нужно.

...