Lua работает в однопоточном режиме, поэтому любая проверка должна быть явно выполнена вашим кодом.
Процесс выполнения кода сразу после изменения переменной известен как «наблюдение».
Если вы программируете в среде, где набор кода выполняется каждый кадр (например, игра), вы можете проверить это вручную.Например:
WatchedVariables = {
a = 5,
b = 22,
}
WatchedVariables_cache = {}
for k,v in pairs(WatchedVariables) do
WatchedVariables_cache[k] = v
end
function OnFrame()
print("NEXT FRAME! (possibly 1 second later or something)")
for k,v in pairs(WatchedVariables) do
local v_old = WatchedVariables_cache[k]
if v ~= v_old then
-- this is the "callback"
print(tostring(k).." changed from "..tostring(v_old).." to "..tostring(v))
WatchedVariables_cache[k] = v
end
end
end
function SomeFunctionThatOperatesSomeTime()
print("about to change a, brother!")
WatchedVariables.a = -7
print("a is changed")
end
После следующего кадра будет выполнен код обратного вызова (печать).Недостатком этого подхода является то, что код обратного вызова не печатается сразу после того, как WatchedVariables.a
установлено на -7
, то есть: вывод будет:
about to change a, brother!
a is changed
NEXT FRAME! (possibly 1 second later or something)
a changed from 5 to -7
Для предотвращенияВ этом потенциально нежелательном поведении может использоваться функция setter , например:
MyObject = {
_private_a = 5,
set_a = function(self, new_value_of_a)
self._private_a = 5
-- callback code
print("a set to "..tostring(new_value_of_a))
end,
get_a = function(self)
return self._private_a
end
}
function SomeFunctionThatOperatesSomeTime()
print("about to change a, brother!")
MyObject:set_a(-7)
print("a is changed")
end
Вывод этого кода показывает, что обратный вызов выполняется немедленно:
about to change a, brother!
a set to -7
a is changed
Чтобы сделать это более удобным, Lua предоставляет metatables , которые делают такое поведение прозрачным для программиста.Пример:
MyObject = {
__privates = {
a = 5,
}
}
MyObject_meta = {
__index = function(self, k)
return rawget(self, "__privates")[k]
end,
__newindex = function(self, k, v)
rawget(self, "__privates")[k] = v
-- callback code
print("a set to "..tostring(v))
end,
}
setmetatable(MyObject, MyObject_meta)
function SomeFunctionThatOperatesSomeTime()
print("about to change a, brother!")
MyObject.a = -7
print("a is changed")
end
Вывод этого кода будет таким же, как в предыдущем примере:
about to change a, brother!
a set to -7
a is changed
Вот реализация для вашего примера:
MyObject = {
__privates = {
a = 5,
}
__private_callback = function(self, k, ov, v)
if k == "a" and v == "100" then
print("a is 100!")
end
end
}
MyObject_meta = {
__index = function(self, k)
return rawget(self, "__privates")[k]
end,
__newindex = function(self, k, v)
local privates = rawget(self, "__privates")
local ov = privates[k]
privates[k] = v
rawget(self, "__private_callback")(self, k, ov, v)
end,
}
setmetatable(MyObject, MyObject_meta)
function SomeFunctionThatOperatesSomeTime()
MyObject.a = -7 -- prints nothing
MyObject.a = 100 -- prints "a is 100!"
MyObject.a = 22 -- prints nothing
end
Почему переменные __privates
и __private_callback
имеют префикс с двумя подчеркиваниями? Общепринятым является префикс закрытых членов, к которым нельзя обращаться в типичных ситуациях программирования, с двумя подчеркиваниями.Если вы знакомы с объектно-ориентированной методологией и ее реализацией на таких языках, как Java и C ++, вы поймете, как она похожа на ключевые слова private
и protected
.
Если вы знакомы с C #языком, вы можете увидеть, как set_a
/ get_a
и метатабельные реализации похожи на средства доступа (set
/ get
).