У Луа нет способа узнать, когда или где был представлен глобал.
В частности, что значение является функцией, debug.getinfo
может помочь, сообщив вам, где определена функция (что часто, но не всегда, является тем же местом, где функция делается глобальной).
Вы можете получить необходимую информацию во время представления глобала. Это может быть сделано путем установки метатаблицы с помощью метода __newindex
в глобальной таблице. Этот метод будет вызываться, когда вводится новый глобал (но не тогда, когда существующий глобал переопределяется). В этом методе вы можете выяснить, откуда пришел вызывающий с debug.getinfo
. Также будьте осторожны, если любой другой ваш код пытается использовать метатаблицу в глобальной среде, вы должны хорошо с ней поиграть. (Он может иметь только одну метатаблицу.)
Вы также можете избежать использования глобальной таблицы. Один из промежуточных способов сделать это - переопределить окружение. В Lua 5.2 и Lua 5.3 это делается путем объявления локальной таблицы с именем _ENV
- вместо этого все обращения к глобальной таблице будут иметь доступ к этой таблице. (На самом деле, глобальные доступы всегда используют _ENV
и _ENV
по умолчанию _G
.) Вы можете сделать это главным образом невидимым, предоставив метатаблице _ENV
, которая перенаправляет доступ к _G
(или любой другой среде). Разница здесь в том, что __newindex
будет по-прежнему вызываться, даже если в _G
существует привязка, поэтому этот метод может обнаруживать переопределения.
Использование _ENV
, хотя по своей природе является локальным для области (например, каждый файл должен переопределять его). Такой крюк может быть установлен и во всем мире. Если вы загружаете свои модули вручную с помощью функции load
(маловероятно), вы можете просто предоставить пользовательский _ENV
в качестве аргумента. Если вы используете require
, можно получить удержание загруженного файла перед его выполнением, переопределив (или установив патчи) поиск Lua в package.searchers[2]
. Это встроенная функция, которую require
вызывает, чтобы найти файл в вашей файловой системе и затем загрузить его. Возвращаемым значением является загруженная функция, которая затем запускается require
. Таким образом, после загрузки, но до возвращения обратно в require
, вы можете использовать debug.setupvalue
для переопределения значения по умолчанию _ENV
(если есть).
Пример кода (только слегка проверенный):
local global_info = {}
local default_searcher2 = package.searchers[2]
package.searchers[2] = function(...)
local result = default_searcher2(...)
local parent_environment = _G
local my_env = setmetatable({}, {
__index = parent_environment,
__newindex = function(self, k, v)
local new_info = debug.getinfo(2)
-- keeping rich data like this could be a memory leak
-- if some globals are assigned repeatedly, but that
-- may still be okay in a debugging scenario
local history = global_info[k]
if history == nil then
history = {}
global_info[k] = history
end
table.insert(history, {info = new_info, value = v})
parent_environment[k] = v
end,
})
if type(result) == "function" then
debug.setupvalue(result, 1, my_env)
end
return result
end
function gethistory(name)
local history = global_info[name]
if history == nil then
print('"' .. name .. '" has never been defined...')
else
print('History for "' .. name .. '":')
for _, record in ipairs(history) do
print(record.info.short_src .. ": " .. record.info.currentline)
end
end
end
Обратите внимание, что здесь ловушка будет применяться только к файлам, требуемым после запуска этого кода, и в основном применяется только к файлам Lua (не C libs) которые включаются через встроенный require
. Он не устанавливает метатаблицы в глобальной среде, поэтому не конфликтует, но его можно обойти, если файлы обращаются к _G
напрямую (или, например, настраивают доступ к _G
вместо _ENV
в своих собственных таблицах _ENV
). Такие вещи также могут быть учтены, но это может быть кроличья нора в зависимости от того, насколько «невидимым» вам нужен этот патч.
В Lua 5.1 вместо _ENV
у вас есть setfenv
, который, я считаю, может быть использован для аналогичного эффекта.
Также обратите внимание, что все методы, которые я выделяю, могут обнаруживать только глобальные обращения, которые фактически выполняются во время выполнения.
Я предполагаю, что это - очевидный случай Шаблонного Шаблона разработки.
Ваш абстрактный класс Разработан для заботы обо всех важных/необходимых требуемых задачах (Здесь, GC.SuppressFinalize (это)), и разрешение производного класса переопределить только некоторую часть кода.
Здесь существует 2 случая:
Отрывок 1, SuppressFinalize, в располагает
Отрывок 2, SuppressFinalize, в располагает (верный)
Здесь, Отрывок 1, удостоверяется, что GC.SuppressFinalize всегда выполняется. В то время как отрывок 2, оставляет выполнение GC.SuppressFinalize во власти производного класса.
Так, путем помещения GC.SuppressFinalize, в Располагают метод, Вы как разработчик Вашего класса будете всегда удостоверяться, что независимо от любого кода, написанного производными классами, GC.SuppressFinalize будет выполняться.
Это - только преимущество записи, что SuppressFinalize в Располагают, скорее затем Располагают (верный).
Я думаю, что любое расположение, возможно, было выбрано, но вероятно они хотели подчеркнуть "помещенный, весь код освобождения в этом методе" в защищенном Располагает метод, таким образом, они помещают другой артефакт расположения (Подавляющий завершение) в другом месте.
Кроме того, предположите, что производный класс имел другую причину вызова защищенного, Располагают метод, но действительно все еще хотел, чтобы завершение произошло (по любой предполагаемой причине, я не знаю).
Dispose(bool isDisposing)
метод не является частью IDisposable
интерфейс.
Вы обычно звонили бы Dispose(true)
от Вашего Dispose
метод и вызов Dispose(false)
от Вашего финализатора, как нейтрализация в случае, где объект не был уже расположен.
Вызов SuppressFinalize
говорит GC, что нет никакой потребности назвать финализатор Вашего объекта, по-видимому, потому что вся Ваша очистка была сделана когда Dispose
был назван.
Если у Вас нет финализатора на Вашем классе, то Вы не должны звонить SuppressFinalize
вообще с тех пор нет никакого финализатора для подавления!
У Joe Duffy есть некоторые большие инструкции по распоряжению, завершению, сборке "мусора" и т.д.
Идея состоит в том, что Ваш код очистки нужно только назвать однажды. Однако существует две точки входа: Dispose
метод и объектные финализаторы. Когда Dispose
назван, Вы уклонение завершения так, чтобы Ваш код очистки только назвали однажды. Код здесь мог бы проиллюстрировать его лучше.
Кавычка:
// NOTE: Leave out the finalizer altogether if this class doesn't
// own unmanaged resources itself, but leave the other methods
// exactly as they are.
Причина.Dispose состоит в том, когда управляемый код (Ваш код) избавляется от объекта, таким образом, выбирая из завершения. Люди обычно создают другой маршрут в, Располагают (bool располагающий) через финализатор, и вызов не имел бы никакого смысла для финализатора делать.