由于目前要把大量的代码移植到 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。

  同时,我做了如下的一些小修改:

  1. 可以直接使用派生类调用积累的静态成员方法,如基类 Base.ShowStatic(),那么派生类可以直接使用:Derived.ShowStatic();
  2. 增加了一个名为 ctor 的可选构造函数(这个借鉴了云风给出的 lua-oop 方案);
  3. 保持通过访问父类方法使用 __base,但注意不用使用这个来访问父类成员变量,因为当你第一次在派生类访问父类变量,会被复制到派生类,所以可能会访问到错误的数据,只有派生类的才是有效的。

  修改完的代码如下:

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。相关推荐

  1. Unity+C#开发笔记(七)| unity中继承MonoBehaviour类的脚本里的函数怎么调用 | ╭(●`∀´●)╯╰(●’◡’●)╮

    继上文,有个蠢货单例模式只是用来引用,突然遇到了一个继承了MonoBehaviour的类,然后unity跟你讲:不!你不可以在我这里这么用! 啊这--吓得我当场百度... ************** ...

  2. Java中继承thread类与实现Runnable接口的区别

    Java中线程的创建有两种方式: 1.  通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2.  通过实现Runnable接口,实例化Thread类 在实际应用中, ...

  3. 多线程中继承Thread 类和实现Runnable 接口的区别

    网上看了很多多线程里继承Thread 类和实现Runnable 接口的区别,大同小异,说下我的观点吧 区别一 创建线程的方式不同: 实现Runnable接口是代理模式,交给Thread()类去star ...

  4. java 继承 意义_Java中继承,类的高级概念的知识点

    1.继承含义 在面向对象编程中,可以通过扩展一个已有的类,并继承该类的属性和行为,来创建一个新的类,这种方式称为继承(inheritance). 2.继承的优点 A.代码的可重用性 B.子类可以扩展父 ...

  5. Java中继承和面向接口的编程

    继承是面向对象中很重要的概念.如果考虑到Java语言特性,继承分为两种:接口继承和实现继承.这只是技术层面的问题,即便C++中不存在接口的概念,但它的虚基类实际上也相当于接口.对于OO的初学者来说,他 ...

  6. python中arcsec_在Python类中继承Cython类

    我有两个用cython编写的类,我想在python中的一个类中使用它们.在 位置.pyximport numpy as np cimport numpy as np cimport cython cp ...

  7. java中继承、多接口实现

    一.子类继承父类,实现接口,父类和接口有同名方法 子类直接继承父类和实现接口不实现接口方法不报错,说明这种情况下默认父类实现该方法 二.一个类实现多个接口,而多个接口中出现同名方法(需要看同名方法的返 ...

  8. java中继承applet类_java.applet.Applet类

    1.applet是一种不能单独运行但可嵌入在其他应用程序中的小程序 2.该类直接继承于java.awt.Panel类,其直接子类为javax.swing.JApplet 3.常用方法 public U ...

  9. Java中已经存在了十几年的一个bug...

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"获取公众号专属群聊入口 来源:rrd.me/gfgTx 今天,分享一个 JDK ...

最新文章

  1. 如何改进yolov3_揭秘YOLOv3鲜为人知的关键细节
  2. C#实现缩放和剪裁图片的方法示例
  3. 邻接矩阵实现图的深度优先搜索(1)
  4. 全球100款大数据工具汇总(前50款)
  5. 圣三一学院计算机专业,360教育集团:爱尔兰都柏林大学圣三一学院计算机专业...
  6. 声明式事务、Spring 中常用注解、Ajax
  7. FTP测试手机软件画画教程图片,手机绘画SketchBook原创教程
  8. 根据多个字段分组_Excel 分组后计算
  9. SQL server 2008 不允许保存更改,您所做的更改要求删除并重新创建以下表 的解决办法...
  10. 改进3D/2D U-NET--添加深度监督deep supervision【Keras】
  11. centos7开启网卡命令_centos7如何启动网卡
  12. 金融学习之十一——久期
  13. python 写文件write(string), writelines(list) ,读文件
  14. 玩转MAC OS!实测DIY兼容机装苹果系统
  15. Only no-arg methods may be annotated with @Scheduled 的解决方法
  16. hive 学习系列五(hive 和elasticsearch 的交互,很详细哦,我又来吹liubi了)
  17. 全球域名商域名增量Top15:51DNS排名降至第十二
  18. 阿里巴巴-新加坡南洋理工大学成立联合研究院 开展全方位AI合作
  19. IBM X3400 m3 EFI platform initialization
  20. 2022网络安全技术及应用复习重点

热门文章

  1. Python 3.3.2 中文版
  2. html特殊字符的html,js,css写法汇总
  3. springboot配置内部配置类
  4. Google-Analytics 学习与思考
  5. 《你说对就队》第四次作业:项目需求调研与分析
  6. [bzoj3676] [APIO2014]回文串
  7. 最近在弄ionic3的时候遇到的一些问题(遇到就更新)
  8. javascript设计模式之观察者模式
  9. Linux的profile与bashrc的分析
  10. ERP笔记2-善用SVN对系统环境进行配置和组织