ПРИМЕЧАНИЕ: Потерпите меня, я чувствую себя немного «зажаренным пламенем» из-за некоторых обсуждений здесь и здесь и некоторых проблем, о которых я сообщил здесь и здесь .
Какой-то фон
Старый (до 10.4) FreeAndNil
выглядел так:
FreeAndNil(var SomeObject)
Новое и fre sh FreeAndNil
выглядит так:
FreeAndNil(const [ref] SomeObject: TObject);
ИМО у обоих есть свои недостатки:
- У старого нет выполнять любую проверку типов, поэтому вызов
FreeAndNil
для указателей, записей и интерфейсов компилируется нормально, но дает интересные, но обычно нежелательные эффекты во время выполнения. (Сходит с ума или, если вам повезет, останавливается с EAccessViolation, EInvalidOperation et c.) - Новый принимает параметр const и, следовательно, любой объект. Но тогда предоставленный указатель объекта фактически изменяется с использованием некоторого хакерского кода .
- Теперь вы можете вызвать новый
FreeAndNil
следующим образом: FreeAndNil(TObject.Create)
, и он будет компилироваться и даже запускаться просто хорошо. Мне понравился старый FreeAndNil
, который предупреждал меня, когда я ошибся, и предоставлял, например, свойство вместо поля. Не знаю, что произойдет, если вы предоставите свойство типа объекта этой реализации FreeAndNil
. Не пробовал.
Если мы изменим подпись на FreeAndNil(var SomeObject:TObject)
, то это не позволит нам передать какой-либо другой тип переменной, а именно тип TObject
. Что также имеет смысл, как если бы это не было FreeAndNil
, можно было бы легко изменить переменную, указанную как тип TComponent
в подпрограмме, заменив переменную var на объект совершенно другого типа, например TCollection
. Конечно, FreeAndNil
этого не сделает, так как он всегда меняет параметр var на nil.
Таким образом, FreeAndNil
становится особым случаем. Может быть, даже достаточно особенным, чтобы убедить delphi, чтобы добавить компилятор magi c FreeAndNil
реализацию? Кто-нибудь голосует?
Возможный обходной путь
Я придумал приведенный ниже код в качестве альтернативы (здесь как вспомогательный метод, но также может быть частью TObject
реализация), которая в своем роде объединяет оба мира. Assert
поможет найти недопустимые вызовы во время выполнения.
procedure TSGObjectHelper.FreeAndNilObj(var aObject);
begin
if Assigned(self) then
begin
Assert(TObject(aObject)=self,ClassName+'.FreeAndNil Wrong parameter provided!');
pointer(aObject):=nil;
Destroy;
end;
end;
Использование будет примерно таким:
var MyObj:=TSOmeObject.Create;
...
MyObj.FreeAndNilObj(MyObj);
Я действительно тестировал эту процедуру, и она даже немного быстрее чем реализация 10,4 FreeAndNil
. Наверное, потому что сначала провожу проверку назначений и напрямую звоню по номеру Destroy
. Что я делаю , а не , так это то, что:
- проверка типа происходит во время выполнения, и только если утверждения включены.
- кажется, что необходимость передать одну и ту же переменную дважды. Что не обязательно верно / обязательно. Это должен быть тот же объект, а параметр должен быть переменной.
Другое расследование
Но было бы здорово, если бы можно было вызов без параметра
var MyObj:=TSomeObject.Create;
...
MyObj.FreeAndNil;
Итак, я испортил указатель self
и смог установить его на nil
, используя тот же код Hacky-Wacky, который 10.4 использует в своих FreeAndNil
. Что ж ... это сработало внутри метода, self
указал на nil
. Но после такого вызова FreeAndNil
переменная MyObj была не nil, а устаревшим указателем. (Это было то, что я ожидал.) Более того, MyObj
может быть свойством или (результатом) процедуры, конструктором и т. Д. c.
, так что nope здесь тоже ...
И, наконец, вопрос:
Можете ли вы придумать более чистое / лучшее решение или трюк, который:
- FreeAndNil (var aObject: TObject) с не очень строгой проверкой типов во время компиляции (возможно, директивой компилятора?), поэтому он позволяет компилировать и вызывать переменные любого типа объекта.
- Жалуется на компиляцию время, когда передается что-то, что является не переменной / полем некоторого типа объекта
- Помощь с описанием наилучшего решения / требования в RSP-29716