Это очень широкий вопрос с разными углами.
Значение функции Assigned
Большая часть кода в вашем вопросе выдает неверныйпонимание функции Assigned
.Документация гласит:
Проверяет ноль (неназначенный) указатель или процедурную переменную.
Использование Назначено , чтобы определить, является ли указатель или процедура, на которую ссылается P, nil .P должен быть ссылкой на переменную указателя или процедурного типа.
Назначено (P) соответствует тесту P <> nil для переменной указателя и @ P <> nil для процедурной переменной.
Назначено возвращает False , если P равно nil , True в противном случае.
Совет : при тестировании событий объекта и процедур для назначения нельзя выполнить проверку на nil и использовать Assigned правильный путь.
....
Примечание : Назначенный не может обнаружить висячий указатель, то есть тот, который не является nil , но это больше не указывает на действительные данные.
Значение Assigned
отличается для указателя и процедурных переменных.В оставшейся части этого ответа мы рассмотрим только переменные-указатели, так как это контекст вопроса.Обратите внимание, что ссылка на объект реализована как переменная-указатель.
Ключевые моменты, которые следует извлечь из документации, для переменных-указателей:
Assigned
эквивалентно тестированию <> nil
. Assigned
не может определить, является ли указатель или ссылка на объект действительным или нет.
Что это означает в контексте этого вопроса, что
if obj<>nil
и
if Assigned(obj)
полностью взаимозаменяемы.
Тестирование Assigned
перед вызовом Free
Реализация TObject.Free
очень особенный.
procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
Это позволяет вам вызывать Free
для ссылки на объект nil
, и это не имеет никакого эффекта.Что бы это ни стоило, я не знаю ни одного другого места в RTL / VCL, где бы использовался такой трюк.
Причина, по которой вы хотели бы разрешить вызов Free
на nil
ссылка на объект проистекает из того, как конструкторы и деструкторы работают в Delphi.
Когда в конструкторе возникает исключение, вызывается деструктор.Это сделано для того, чтобы освободить любые ресурсы, которые были выделены в той части конструктора, которая преуспела.Если Free
не был реализован как есть, то деструкторы должны были бы выглядеть следующим образом:
if obj1 <> nil then
obj1.Free;
if obj2 <> nil then
obj2.Free;
if obj3 <> nil then
obj3.Free;
....
Следующий фрагмент головоломки состоит в том, что Delphi-конструкторы инициализируют память экземпляра в ноль ,Это означает, что любые неназначенные ссылочные поля объекта nil
.
Соберите все это вместе, и код деструктора теперь станет
obj1.Free;
obj2.Free;
obj3.Free;
....
Вам следует выбрать последний вариант, потому что он намного более читабелен.
В одном сценарии вынужно проверить, назначена ли ссылка в деструкторе.Если вам нужно вызвать какой-либо метод объекта перед его уничтожением, тогда вы должны быть уверены, что он не может быть nil
.Таким образом, этот код рискует получить AV, если он появится в деструкторе:
FSettings.Save;
FSettings.Free;
Вместо этого вы пишете
if Assigned(FSettings) then
begin
FSettings.Save;
FSettings.Free;
end;
Тестирование Assigned
вне деструктора
Вы также говорите о написании защитного кода вне деструктора.Например:
constructor TMyObject.Create;
begin
inherited;
FSettings := TSettings.Create;
end;
destructor TMyObject.Destroy;
begin
FSettings.Free;
inherited;
end;
procedure TMyObject.Update;
begin
if Assigned(FSettings) then
FSettings.Update;
end;
В этой ситуации снова нет необходимости проверять Assigned
в TMyObject.Update
.Причина в том, что вы просто не можете вызвать TMyObject.Update
, если конструктор TMyObject
не был выполнен успешно.И если конструктор TMyObject
завершился успешно, вы наверняка знаете, что FSettings
был назначен.Итак, снова вы делаете свой код гораздо менее читабельным и трудным для поддержки, вводя ложные вызовы Assigned
.
Существует сценарий, в котором вам нужно написать if Assigned
, и именно здесь существование рассматриваемого объекта необязательно. Например
constructor TMyObject.Create(UseLogging: Boolean);
begin
inherited Create;
if UseLogging then
FLogger := TLogger.Create;
end;
destructor TMyObject.Destroy;
begin
FLogger.Free;
inherited;
end;
procedure TMyObject.FlushLog;
begin
if Assigned(FLogger) then
FLogger.Flush;
end;
В этом сценарии класс поддерживает два режима работы, с и без регистрации. Решение принимается во время построения, и любые методы, которые ссылаются на объект каротажа, должны проверить его существование.
Эта не редкая форма кода делает еще более важным, чтобы вы не использовали ложные вызовы Assigned
для необязательных объектов. Когда вы видите if Assigned(FLogger)
в коде, это должно быть для вас четким признаком того, что класс может нормально работать с FLogger
, которого не существует. Если вы распыляете бесплатные звонки на Assigned
вокруг вашего кода, то вы теряете возможность сразу определить, должен ли объект существовать всегда.