通过 __index 进行元方法查找?
我实现了自己的类系统,但是我在__tostring
方面遇到了困难。 我怀疑其他元方法也可能发生类似的问题,但我还没有尝试过。
(简要介绍一下:每个类都有一个__classDict
属性,用于存储所有方法。它被用作类实例的__index
。同时,__classDict的__index
是超类的__classDict
,因此可以自动查找超类中的方法。)
我想在所有实例中都有一个“默认 tostring”行为。但是它没起作用:子类中的“tostring”行为没有正确地“传播”。
我通过以下测试来说明我的问题:
mt1 = {__tostring=function(x) return x.name or "no name" end }
mt2 = {}
setmetatable(mt2, {__index=mt1})
x = {name='x'}
y = {name='y'}
setmetatable(x, mt1)
setmetatable(y, mt2)
print(x) -- prints "x"
print(mt2.__tostring(y)) -- prints "y"
print(y) -- prints "table: 0x9e84c18" !!
我希望最后一行打印“y”。
Lua的“to_String”行为必须使用等效的方式
rawget(instance.class.__classDict, '__tostring')
而不是相当于
instance.class.__classDict.__tostring
我怀疑所有元方法都会发生同样的情况;使用类似于rawget
的操作。
我想我可以在进行子类化时复制所有的元方法(上面例子的等效方法将是执行mt2.__tostring = mt1.__tostring
),但这有点不优雅。
有没有人遇到过这种问题?你们的解决方案是什么?
原文链接 https://stackoverflow.com/questions/3276098
我怀疑所有元方法都会发生同样的事情;会使用相当于 rawget
的操作。
这是正确的。 根据 Lua 手册说明:
…应该被理解为
rawget(getmetatable(obj) or {}, event)
。也就是说,对元方法的访问不会调用其他元方法,对没有元表的对象的访问不会失败 (仅仅返回 nil)。
通常,每个类都有自己的元表,你需要将所有对函数的引用复制到其中。
也就是说,写成 mt2.__tostring = mt1.__tosting
。
感谢daurnimator的评论,我认为我找到了一种让元方法“遵循”__index
的方法。这个函数如下:
local metamethods = {
'__add', '__sub', '__mul', '__div', '__mod', '__pow', '__unm', '__concat',
'__len', '__eq', '__lt', '__le', '__call', '__gc', '__tostring', '__newindex'
}
function setindirectmetatable(t, mt)
for _,m in ipairs(metamethods) do
rawset(mt, m, rawget(mt,m) or function(...)
local supermt = getmetatable(mt) or {}
local index = supermt.__index
if(type(index)=='function') then return index(t,m)(...) end
if(type(index)=='table') then return index[m](...) end
return nil
end)
end
return setmetatable(t, mt)
end
我希望这很明显。当设置一个新的元表时,它初始化了所有的元方法(不会替换现有的元方法)。这些元方法被准备好“传递”请求到“父元表”。
这是我能找到的最简单的解决方案。嗯,实际上我找到了一个使用更少字符且更快的解决方案,但它涉及黑魔法(它涉及在自己的主体内取消引用元表函数),并且比这个解决方案可读性差得多。
如果有人发现一个更简短、更简单的函数,做同样的事情,我会很乐意给他答案。
使用很简单:当你想要“向上”时,用setindirectmetatable
替换setmetatable
:
mt1 = {__tostring=function(x) return x.name or "no name" end }
mt2 = {}
setmetatable(mt2, {__index=mt1})
x = {name='x'}
y = {name='y'}
setmetatable(x, mt1)
setindirectmetatable(y, mt2) -- 仅在代码中更改
print(x) -- 打印 "x"
print(mt2.__tostring(y)) -- 打印 "y"
print(y) -- 打印 "y"
一个小小的警告:setindirectmetatable
在mt2上创建了元方法。改变这个行为,使它创建一个副本,mt2保持不变,应该是轻而易举的。但是默认让它们设置是更好的。
根据我对 Lua 5.1 的经验,metamethods 是使用 rawget() 在元表中查找的,这就是为什么您必须将函数的引用复制到您创建的每个类表中的原因。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
请在 Lua 用户维基上查看继承教程。