Сохранить наследуемый член таблицы в дочерний класс, если __index вызывается объектом дочернего класса

Моя цель состоит в том, чтобы иметь стандартный способ создания классов с полноразмерным множественным наследованием и возможностью не только наследовать другие классы, но и экземпляры самих себя через new()-конструктор. Если я вызываю отсутствующее значение из класса или из его объекта, и это значение является функцией, я хотел бы сохранить его в классе для последующего использования, но не в объекте / экземпляре (проблемы с производительностью при реализации той же функции в массовом количество предметов класса).

На данный момент я использую createClass(...) -функция, очень похожая на ту, которая упоминается в официальной серии учебников ( http://www.lua.org/pil/16.3.html), для создания классов, которые наследуются практически от любого числа родительских классов:

-- look up for k in list of tables plist
local function search (k, plist)
    for i=1, #plist do
        local v = plist[i][k]     -- try i-th superclass
        if v then return v end
    end
end

local function createClass (...)
    local args = {...};
    local c = {};
    -- search all provided parent classes for variables/functions to inherit and include them
    -- could be done without including them (deeply nested inheritance might be an issue this way
    -- due to repeated search calls for each use of an inherited function)
    setmetatable(c, {__index = function (t, k)
        local v = search(k, args);
        t[k] = v;
        print(t, " ...looking for... ", k, v);
        return v;
    end})
    return c;
end

Он передает индекс-метаметод недавно созданному классу, который ищет во всех родителях указанный отсутствующий ключ и сохраняет его для последующего использования в вызывающем классе. Это работает как намерение, даже при поиске огромной иерархии вложенного наследования. Теперь давайте введем простой класс с базовым конструктором:

local P = {};
local MyClass = P;

function P.new ()
    local self = {};
    local priv = {};
    setmetatable(self, {__index = function (t, k)
        -- shouldn't this invoke __index-metamethod of P because P does not already have k
        -- at this stage of the program?
        local v = P[k];
        print(t, " ...looking for ... ", k, v);
        if(type(v) == "function") then
            -- ??? maybe do somethine else here to enforce __index of class P
        else
            t[k] = v;
        end
        return v;
    end});

    self.testSelf = function () print("testSelf") end;
    priv.testPriv = "testPriv!";

    function self.accessPriv ()
        return priv;
    end

    return self;
end

function P.TEST ()
    print("calling TEST");
end

Если мы вызовем эту реализацию наследования и класса с помощью следующего кода, то мы увидим, что __index -метаметод класса не вызывается при создании нового экземпляра класса (сравните адреса выходных таблиц), даже если __index -метаметод объекта запрашивает отсутствующее значение / функцию из его (родительского) класса, который не имеет такого значения / функции на данный момент. По-видимому, это описание не вызывает __index -метаметод самого класса, который меня смущает. Я бы предположил, что он всегда вызывается, если запрашивается пропущенное значение класса / таблицы, независимо от того, из какой части скрипта.

  1. Почему __index -метаметод из MyClass не вызывается в данном примере?
  2. И как я могу сделать это (делая как можно меньше изменений в коде)?

Ваши предложения приветствуются!

1 ответ

Решение

Я нашел ответ, не понимая его полностью. Оказывается, при вызове конструктора класса newТребуется передать ему сам класс. Таким образом, я могу напрямую манипулировать переменной, которая представляет класс. До сих пор остается путаница в отношении того, почему я могу наследовать от класса и его родителей экземпляр / объект класса, но не могу получить доступ к классу для какой-либо другой операции в объектах. __index-metamethod.

Это определение класса, наконец, делает свою работу:

local P = {};
local MyClass = P;

-- must pass class itself while creating new object of it (don't know why...)
function P:new ()
    local pub = {};
    local priv = {};
    setmetatable(pub, {__index = function (t, k)
        local v = P[k];
        -- store functions into the class for later use (less duplicates)
        -- and all other inherited variables into the objects itself
        if (type(v) == "function")
            self[k] = v;
        else
            t[k]    = v;
        end
        return v;
    end});

    pub.testSelf = function () print("testSelf") end;
    priv.testPriv = "testPriv!";

    function pub.accessPriv ()
        return priv;
    end

    return pub;
end

function P.TEST ()
    print("calling TEST");
end
Другие вопросы по тегам