Подтвердить что ты не робот

Откуда взялась переменная = null как "разрушение объекта"?

Работая над несколькими устаревшими системами, написанными в различных версиях .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, которое я не получил?

4b9b3361

Ответ 1

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

Некоторые GC не справились с круговыми ссылками. Чтобы избавиться от них, вам пришлось сломать цикл "где-то". Где? Ну, если есть сомнения, то везде. Сделайте это в течение года, и это движется в ваши руки.

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

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

Ответ 2

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

Public Sub Main()
  Dim big As Variant
  Set big = GetReallyBigObject()
  Call big.DoSomething
  Set big = Nothing
  Call TimeConsumingOperation
  Call ConsumeMoreMemory
End Sub

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

Ответ 3

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

После вызова free():

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

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

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

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

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;
}

Заключение: IT полностью не требуется в С#, Java и практически любом другом языке сбора мусора.

Ответ 9

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

Ответ 10

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

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; 
    } 
    vavoom(foo,bar);
} 

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

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

Ответ 11

Разработчики VB должны были утилизировать все свои объекты, чтобы попытаться уменьшить вероятность утечки памяти. Я могу себе представить, откуда это произошло, поскольку разработчики VB перешли на .NEt/С#

Ответ 12

Я вижу, что это происходит из-за неправильного понимания того, как работает сборщик мусора, или с целью заставить GC немедленно начать - возможно, потому, что объекты foo и bar довольно большие.

Ответ 13

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

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

Ответ 14

Он исходит из кода С++, особенно умных указателей. В этом случае он грубо эквивалентен .Dispose() в С#.

Это не очень хорошая практика, не более инстинкт разработчика. Нет реального значения, назначая null в С#, но может помочь GC разбить круговую ссылку.