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