SLua 中继承 C# 类接口 Slua.Class 的一个 Bug。
由于目前要把大量的代码移植到 lua 中(真是够虐心的),面向对象肯定少不了,项目的代码都是这么设计的,于是就测试 Slua.Class 接口来扩展 C# 的类,发现有点问题,给作者提交了一个 Issue 和 一个 Pull Request,作者也很快确认并 Merge 了。
问题是这样:当使用 Slua.Class 继承出来的类,实例化出来的所有实例都指向了最后一个实例,导致访问属性都是一样的。比如使用 main.txt 中得一段代码修改测试:
-- test inherite class local mv = My2(1, 2, 3) local mv_2 = My2(4, 5, 6) -- I add for test. mv:Normalize() mv_2:Normalize() -- I add for test. print("mv norm:", mv.x, mv.y, mv.z) -- I modified for test. print("mv_2 norm:", mv_2.x, mv_2.y, mv_2.z) -- I add for test. mv:Set(10, 20, 30) -- I modified for test. mv_2:Set(40, 50, 60) -- I add for test. print("mv:", mv.x, mv.y, mv.z) -- I add for test. print("mv_2:", mv_2.x, mv_2.y, mv_2.z) -- I add for test.结果将输出如下:
mv norm: 0.62469504755442 0.78086880944303 0.93704257133164 mv_2 norm: 0.62469504755442 0.78086880944303 0.93704257133164 mv: 40 50 60 mv_2: 40 50 60在以上结果中,My2 的实例 my, my_2 构造的值是不同的,但输出相同的结果。看看 Slua.Class 的代码,在 Helper.cs 中:
local getmetatable=getmetatable local function Class(base,static,instance)local mt = getmetatable(base)local class=static or {}setmetatable(class, {__call=function(...)local r = mt.__call(...)local ret = instance or {}ret.__base=rlocal ret = setmetatable(ret,{__index=function(t,k)return r[k]end,__newindex=function(t,k,v)r[k]=vend,})return retend,})return class end return Class以上代码中,ret 是类的模板,用来为各个实例化对象提供方法和属性,不应该被构造时返回(而且上面每次构造都返回了相同的一个 ret),但是 ret 应该是大家 shaderd,构造返回的对象应该是一个新构造的对象,且 __index 为 ret,这样既能获取派生类的各种方法属性,又不会不小心修改 ret。
同时,我做了如下的一些小修改:
修改完的代码如下:
local getmetatable = getmetatable local function Class(base,static,instance)local mt = getmetatable(base)local class = static or {}setmetatable(class, {__index = base,__call = function(...)local r = mt.__call(...)local ret = instance or {}local ins_ret = setmetatable({__base = r,},{__index = function(t, k)local ret_fieldret_field = ret[k]if nil == ret_field thenret_field = r[k]endt[k] = ret_fieldreturn ret_fieldend,})if ret.ctor thenret.ctor(ins_ret, ...)endreturn ins_retend,})return class end return Class使用跟以前一样,但可以增加一个构造函数:
MyVector3 = Slua.Class(Vector3, { }, {-- This is optional.ctor = function(self)print("Do something...")end, })但是我觉得还是有点小问题,以上书写新的扩展类代码的时候不是太方便,不能分开单独写每个成员变量和函数,也可以墙纸分开,但命名上不太好看,于是我自己又做了如下修改:
local getmetatable = getmetatable local function Class(base)local mt = getmetatable(base)local class = {}class.ctor = falsesetmetatable(class, {__index = base,__call = function(...)local r = mt.__call(...)local ins_ret = {__base = r,}setmetatable(ins_ret,{__index = function(t, k)local ret_fieldret_field = rawget(class, k)if nil == ret_field thenret_field = r[k]if 'function' == type(ret_field) thenclass[k] = ret_fieldelseins_ret[k] = ret_fieldendendreturn ret_fieldend,})if class.ctor thenclass.ctor(ins_ret, ...)endreturn ins_retend,})return class end return Class这样的话,我就可以更方便的定义类,符合以前的书写习惯,同时,优化一下,当访问派生类不存在的的父类成员时,之拷贝函数,不拷贝成员变量,以免浪费空间。这样我可以这样书写:
MyVector3 = Slua.Class(Vector3)-- Constructor, optional. function MyVector3:ctor()print("Do something!") end-- Instance method. function MyVector3:Normaize()--Do your own normalize. end-- Static method. function MyVector3.PrintMyNameprint("MyVector3") end但作者说如果不是 bug,只是为了方便,最后这个不能修改,因为要考虑兼容性,已经有人这么用了,确实是这样,所以我就把这个提交到自己的另一个分支里,在自己的项目使用新方法。
转载于:https://www.cnblogs.com/yaukey/p/4545093.html
总结
以上是生活随笔为你收集整理的SLua 中继承 C# 类接口 Slua.Class 的一个 Bug。的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 黑马程序员_Java面向对象_包
- 下一篇: C#根据execl批量修改图片名称