【语法算法】
rawlen 的本质就这一行:
local n = rawlen(t)
rawlen 绕过 __len 元方法直接返回表的原始长度——rawlen 的全部语义就这一行。可这一行的存在意义在于:当表设了 __len 元方法时,#t 走元方法而 rawlen 走原始操作——两者可能返回不同的值。今天拆开。
一、原始、元方法、分野三段
rawlen 的三段。原始:rawlen 直接返回表的数组部分长度——不查元表不调 __len——原始操作就是"无中间人"。元方法:#t 如果表的元表有 __len——走 __len 元方法返回自定义长度——原始长度被覆盖。分野:rawlen 和 #t 的分野在"设了 __len 的表"——没设元方法的表两者返回相同值——设了 __len 的表两者可能不同。三段连起来:rawlen 玩的是"绕过抽象层直接看底层"——调试和性能分析时 rawlen 比 # 更诚实。
graph TD
A["rawlen(t)"] --> B["直接返回数组长度"]
C["#t"] --> D{"t有__len?"}
D -->|是| E["调用__len(t)"]
D -->|否| F["返回数组长度"]
E --> G["返回自定义长度"]
二、rawlen 的正确写法
local container = setmetatable({ data = {1, 2, 3} }, {
__len = function(t) return #t.data * 2 end -- 故意翻倍
})
print(#container) -- 6: 走__len返回翻倍值
print(rawlen(container)) -- 0: container表自身没有数组部分
print(rawlen(container.data)) -- 3: data表的真实长度
rawlen 对 container 返回 0——因为 container 表的数组部分为空——__len 元方法返回的是 data 表的长度乘 2——rawlen 看到的是原始结构不是抽象层。
三、rawlen 的定位与调试的武器
rawlen 的定位是调试武器而非业务工具:业务代码永远用 # 操作符——rawlen 只在调试和性能分析时使用——用 rawlen 替代 # 是语义错误,两者返回值可能不同。和 rawget/rawset 的家族关系也值得记:rawget 绕过 __index 直读、rawset 绕过 __newindex 直写、rawlen 绕过 __len 直量——raw 家族的三个函数共同组成"绕过元表的原始操作集"——调试时用 raw 家族逐层排查,就能定位问题出在哪一层元方法。最后一条纪律:rawlen 只对表生效——对字符串 rawlen 会报错——字符串的长度用 #str 或 string.len(str)——不要混用 rawlen 和 string.len。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 再抛一个老问题:奶量是固定好还是浮动好?固定回五十的术式,满血喝是五十、残血喝也是五十,账目清楚,可总差点意思…
【游戏功能】 前阵子一场"永动机事故"在群里传了半天:某个会喷火的精英怪被玩家围了一整晚,火喷了八个钟头没停——后台一看,它…
【游戏功能】 mode=mode==='red'?'blue':'red' ——先拆这一行。一个变量存着当前纹色,三元运算符…
【游戏功能】 上一版埋了个查了半宿的隐蔽报错:测试组报"玩家血条归零了人还站着",后台的死亡日志一条没有,复活接口也没被调过…
【游戏功能】 先抛一个坑:增益为什么只能自己挂?怒气、护体、神行,攻略里全在教你"什么时候开自己的招",可怪身上明晃晃挂着的…
【游戏功能】 昨天夜里一场线上事故把"反纹"顶上了台面:一个玩家把反纹挂在自己身上站桩挂机,怪的火球过来他没倒,反倒躺下的是…