Какой смысл локальной переменной в присоединенном выражении Эйфеля?
В Eiffel Void Safety - это способ статически предотвратить разыменование неинициализированных ("нулевых") объектов. Это работает так, что сначала объект должен быть объявлен как отделяемый, а затем вам нужно проверить в блоке if, действительно ли объект присоединен (то есть имеет какое-то значение), прежде чем вы сможете его использовать.
Вот как я использовал его до сих пор:
some_object: detachable TYPE
...
if attached some_object then
some_object.method
end
Работает отлично: без вложенной проверки компиляция завершается с ошибкой "Target of the Object_call может быть недействительным". Однако, после прочтения документации по Void Safety, я узнал, что на самом деле это выглядит так:
some_object: detachable TYPE
...
if attached some_object as l_some_object then
l_some_object.method
end
В этой форме l_some_object
является локальной переменной для блока if, который указывает на тот же объект, что и some_object
но статически гарантированно не является недействительным.
Тем не менее, я не вижу причины существования этого пункта. Как я уже говорил выше, по-видимому, оригинал some_object
уже статически гарантировано, что он не является пустым в блоке if, так какой смысл вводить другую переменную?
Каковы различия между some_object
а также l_some_object
кроме области?
1 ответ
Короткий ответ
Если some_object
является локальной переменной, нет смысла вводить локальный объектный тест l_some_object
,
Длинный ответ
Общая форма объектного теста
attached {SOME_TYPE} expr as var
где {SOME_TYPE}
а также var
являются необязательными. Когда тип ({SOME_TYPE}
в приведенном выше примере) не используется, объектный тест просто проверяет, expr
прикреплен или нет и присваивает его значение var
когда он прикреплен.
В теории можно ожидать, что что-то вроде следующего будет безопасным для пустот:
if attached expr then
expr.do_something
end
Однако это не допускается в общем случае, потому что expr
может иметь побочные эффекты, так что при втором вычислении возвращается другое значение, и это значение может быть void
сделать код недействительным:
if attached foo then -- On first call function foo returns non-void value.
foo.do_something -- On second call function foo returns void: BOOM!
end
Другой возможностью является промежуточный вызов, который изменяет значение выражения, например,
if attached attr then -- Attribute attr is attached here.
bar -- bar sets attr to Void.
attr.do_something -- BOOM!
end
Если bar
устанавливает атрибут attr
в void
(это может быть сделано косвенно), код снова становится небезопасным.
Наконец, в многопоточной среде другой поток может изменить значение attr
после проверки и перед ее использованием внутри части "then" даже без какого-либо промежуточного вызова функции:
if attached attr then -- Attribute attr is attached here.
-- Another thread sets attr to Void.
attr.do_something -- BOOM!
end
Чтобы предотвратить эти ситуации, var
часть используется. Этот локальный объектный тест доступен только для чтения и не зависит от оценки того же выражения, какого-либо промежуточного вызова функции или другого потока. Другими словами это всегда прилагается.
Тем не менее, в некоторых ситуациях на выражение объектных тестов не влияют следующие факторы:
Аргументы доступны только для чтения, поэтому всегда достаточно использовать краткую форму
attached arg
и нет смысла вводить объектный тест локально, потому что он всегда будет равен аргументу.
Локальные переменные и
Result
может только статьVoid
если им присваивается отрывное выражение. Если такого назначения нет, то жеattached local_var
просто отлично. Однако, как только локальному назначается отрывное выражение, оно больше не считается присоединенным:
if attached local_var then ... -- OK to use local_var as attached. local_var := detachable_expression ... -- No guarantees about local_var attachment status. end
Если этот сценарий нежелателен, можно использовать длинную форму объектного теста
attached local_var as attached_local_var
и это гарантирует, что
attached_local_var
всегда прилагается