【问题标题】:Inheritance for metamethods in LuaLua中元方法的继承
【发布时间】:2010-11-11 15:09:53
【问题描述】:

我非常喜欢“lua 编程”16.1、16.2 中对面向对象编程的描述:

http://www.lua.org/pil/16.1.html

http://www.lua.org/pil/16.2.html

并希望遵循这种方法。但我想更进一步:我想要一个名为“class”的基“类”,它应该是所有子类的基础,因为我想在那里实现一些辅助方法(比如“instanceof”等.),但本质上应该如书中所述:

function class:new(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end

现在解决我的问题:

我想要一个类“number”,它继承自“class”:

number = class:new()

我想为此类中的运算符重载(__add、__sub 等)定义元方法,例如:

n1 = number:new()
n2 = number:new()

print(n1 + n2)

有效。这不是一个真正的问题。但现在我想要第三类“money”,它继承自“number”:

money = number:new{value=10,currency='EUR'}

我在这里介绍一些新的属性等等。

现在我的问题是,我没有让事情发挥作用,“金钱”继承了“类”和“数字”的所有方法包括“数字”中定义的所有元方法。

我已经尝试了几件事,例如覆盖“new”或修改元表,但我无法让事情正常工作,没有丢失“money”中的“class”方法或丢失“number”中的元方法“钱”

我知道,那里有很多类实现,但我真的很想坚持使用 lua 本身的最小化方法。

任何帮助将不胜感激!

非常感谢!

【问题讨论】:

    标签: oop inheritance lua


    【解决方案1】:

    我认为您遇到的问题是由于使用类似于rawget(getmetatable(obj) or {}, "__add") 的东西查找运算符元方法。因此,运算符不会与其他函数一起继承。

    我在让new 函数像这样复制运算符方面取得了一些成功:

    function class:new(o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        local m=getmetatable(self)
        if m then
            for k,v in pairs(m) do
                if not rawget(self,k) and k:match("^__") then
                    self[k] = m[k]
                end
            end
        end
        return o
    end
    

    【讨论】:

    • 很好的解决方案。为了避免在所有基类中重复这一点,我为所有基类创建了一个顶级Class。实现这项工作的技巧是将o = Class.new(self) 添加到它们的构造函数中,以防它们覆盖构造函数。
    【解决方案2】:

    这个问题已经得到解答,但让我介绍一下我的解决方案,这是我前段时间在开发我的 OOP 库时遇到的,middleclass

    就我而言,我开始列出所有“有用的”元方法名称:

    local _metamethods = { -- all metamethods except __index
      '__add', '__call', '__concat', '__div', '__le', '__lt', '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm'
    } 
    

    我使用该列表为创建的每个类添加方法。因此,实际上,我对所有元方法都有一个“默认实现”:

    -- creates a subclass
    function Object.subclass(theClass, name)
      ...
    
      local dict = theSubClass.__classDict -- classDict contains all the [meta]methods of the 
      local superDict = theSuperClass.__classDict -- same for the superclass
      ...
    
      for _,mmName in ipairs(_metamethods) do -- Creates the initial metamethods
        dict[mmName]= function(...) -- by default, they just 'look up' for an implememtation
          local method = superDict[mmName] -- and if none found, they throw an error
          assert( type(method)=='function', tostring(theSubClass) .. " doesn't implement metamethod '" .. mmName .. "'" )
          return method(...)
        end
      end 
    

    诀窍在于默认实现“调用”父类的实现;如果不存在,则会引发错误。

    以这种方式实现的元方法仅比常规方法稍慢(2 个额外的方法调用),并且使用的内存量非常小(每个类 12 个额外的函数)。

    PS:我没有包含 __len 元方法,因为 Lua 5.1 无论如何都不尊重它。

    PS2:我没有包含__index__newindex,因为我必须在我的课程内部使用它们。

    【讨论】:

    • 感谢分享……我会仔细看看你的方法。
    【解决方案3】:

    我做了类似的事情并且遇到了类似的问题。这是我的基类定义:

    RootObjectType = {}
    RootObjectType.__index = RootObjectType
    function RootObjectType.new( o )
            o = o or {}
            setmetatable( o, RootObjectType )
            o.myOid = RootObjectType.next_oid()
            return o
    end
    
    function RootObjectType.newSubclass()
            local o = {}
            o.__index = o
            setmetatable( o, RootObjectType )
            RootObjectType.copyDownMetaMethods( o, RootObjectType )
            o.baseClass = RootObjectType
            return o
    end
    
    function RootObjectType.copyDownMetaMethods( destination, source ) -- this is the code you want
            destination.__lt = source.__lt
            destination.__le = source.__le
            destination.__eq = source.__eq
            destination.__tostring = source.__tostring
    end
    
    RootObjectType.myNextOid = 0
    function RootObjectType.next_oid()
            local id = RootObjectType.myNextOid
            RootObjectType.myNextOid = RootObjectType.myNextOid + 1
            return id
    end
    
    function RootObjectType:instanceOf( parentObjectType )
            if parentObjectType == nil then return nil end
            local obj = self
            --while true do
            do
                    local mt = getmetatable( obj )
                    if mt == parentObjectType then
                            return self
                    elseif mt == nil then
                            return nil
                    elseif mt == obj then
                            return nil
                    else
                            obj = mt
                    end
            end
            return nil
    end
    
    
    function RootObjectType:__lt( rhs )
            return self.myOid < rhs.myOid
    end
    
    function RootObjectType:__eq( rhs )
            return self.myOid == rhs.myOid
    end
    
    function RootObjectType:__le( rhs )
            return self.myOid <= rhs.myOid
    end
    
    function RootObjectType.assertIdentity( obj, base_type )
            if obj == nil or obj.instanceOf == nil or not obj:instanceOf( base_type ) then
                    error( "Identity of object was not valid" )
            end
            return obj
    end
    
    function set_iterator( set )
            local it, state, start = pairs( set )
            return function(...) 
                    local v = it(...)
                    return v
            end, state, start
    end
    

    【讨论】:

      猜你喜欢
      • 2021-08-22
      • 2011-10-18
      • 2010-12-21
      • 2014-06-10
      • 1970-01-01
      • 1970-01-01
      • 2011-10-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多