Lua遍历对象列表调用成员方法时报索引数值类型错误
问题根因
报错由两处逻辑错误触发,其中直接导致运行崩溃的是表遍历语法使用错误,另一处是类实现的隐藏bug:
- 直接报错原因:Lua 中用
pairs/ipairs遍历表时,for 循环的第一个返回值是当前元素的键(数组场景下为数字索引),第二个返回值才是键对应的存储值。你写的for projectile in pairs(self.projectiles) do仅接收了第一个返回值,也就是1、2、3这类数字索引,后续对数字调用:move()方法自然会抛出「尝试索引number类型值」的错误。 - 隐藏的原型污染bug:两个类的
new构造函数中,你把传入的属性都赋值给了self(也就是类原型表),而非新创建的实例对象o。这会导致所有创建的实例共享同一组属性,后续修改任意实例的坐标、速度,所有投射物的属性都会被同步篡改。 - 额外的边界bug:
ProjectileGroup初始定义的默认projectiles值为数字0,如果创建组时不传入projectiles参数,后续调用add_projectile执行table.insert会因为传入数字类型参数直接报错。
修复方法
1. 修正遍历逻辑
遍历表时同时接收索引和值两个返回值,不需要使用索引时可以用_做占位符。连续索引的数组场景优先用ipairs遍历,行为更稳定、性能更好:
function ProjectileGroup:move() for _, projectile in ipairs(self.projectiles) do projectile:move() end end function ProjectileGroup:log() for _, projectile in ipairs(self.projectiles) do projectile:log() end end
2. 修正构造函数赋值逻辑
构造函数中所有实例属性都要赋值到新创建的实例表o上,不要修改类原型self,同时给projectiles设置合理的空表默认值:
function Projectile:new (o,x,y,z,six,siy,siz,spx,spy,spz) o = o or {} setmetatable(o, self) self.__index = self -- 所有属性绑定到实例o o.x = x o.y = y o.z = z o.six = six o.siy = siy o.siz = siz o.spx = spx o.spy = spy o.spz = spz return o end function ProjectileGroup:new (o,projectiles) o = o or {} setmetatable(o,self) self.__index = self -- 未传入projectiles时默认初始化为空表 o.projectiles = projectiles or {} return o end
修复后验证代码
替换上述两段逻辑后即可正常运行,调用projectileGroup:log()可以看到三个投射物的坐标已经按照各自的速度正确更新:
--Projectile class Projectile = {x = 0, y = 0, z = 0, six = 0, siy = 0, siz = 0, spx = 0, spy = 0, spz = 0} function Projectile:new (o,x,y,z,six,siy,siz,spx,spy,spz) o = o or {} setmetatable(o, self) self.__index = self o.x = x o.y = y o.z = z o.six = six o.siy = siy o.siz = siz o.spx = spx o.spy = spy o.spz = spz return o end function Projectile:move () self.x = self.x + self.spx self.y = self.y + self.spy self.z = self.z + self.spz end function Projectile:log () system.print('x: '..tostring(self.x)..'|y: '..tostring(self.y)..'|z: '..tostring(self.z)..'|six :'..tostring(self.six)..'|siy :'..tostring(self.siy)..'|siz :'..tostring(self.siz)..'|spx :'..tostring(self.spx)..'|spy :'..tostring(self.spy)..'|spz :'..tostring(self.spz)) end --ProjectileGroup ProjectileGroup = {} function ProjectileGroup:new (o,projectiles) o = o or {} setmetatable(o,self) self.__index = self o.projectiles = projectiles or {} return o end function ProjectileGroup:add_projectile(projectile) table.insert(self.projectiles, projectile) end function ProjectileGroup:move() for _, projectile in ipairs(self.projectiles) do projectile:move() end end function ProjectileGroup:log() for _, projectile in ipairs(self.projectiles) do projectile:log() end end projectileA = Projectile:new(nil,0,0,0,10,10,10,0.1,0.3,0.4) projectileB = Projectile:new(nil,0,0,0,10,10,10,0.4,0.2,-0.4) projectileC = Projectile:new(nil,0,0,0,10,10,10,0.1,0.05,0.1) projectileGroup = ProjectileGroup:new(nil,{projectileA,projectileB,projectileC}) projectileGroup:move() projectileGroup:log()
内容的提问来源于stack exchange,提问作者Mathieu Bragard
相关产品推荐
相关产品推荐

