由于目前要把大量的代码移植到 lua 中(真是够虐心的),面向对象肯定少不了,项目的代码都是这么设计的,于是就测试 Slua.Class 接口来扩展 C# 的类,发现有点问题,给作者提交了一个 Issue 和 一个 Pull Request,作者也很快确认并 Merge 了。

  问题是这样:当使用 Slua.Class 继承出来的类,实例化出来的所有实例都指向了最后一个实例,导致访问属性都是一样的。比如使用 main.txt 中得一段代码修改测试:

-- test inherite class
local mv = My2(, , )
local mv_2 = My2(, , ) -- 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(, , ) -- I modified for test.
mv_2:Set(, , ) -- 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=r
local ret = setmetatable(ret,{
__index=function(t,k)
return r[k]
end,
__newindex=function(t,k,v)
r[k]=v
end,
})
return ret
end,
}
)
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_field
ret_field = ret[k]
if nil == ret_field then
ret_field = r[k]
end
t[k] = ret_field
return ret_field
end,
})
if ret.ctor then
ret.ctor(ins_ret, ...)
end
return ins_ret
end,
}
)
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 = false
setmetatable(class,
{
__index = base,
__call = function(...)
local r = mt.__call(...)
local ins_ret = {__base = r,}
setmetatable(ins_ret,
{
__index = function(t, k)
local ret_field
ret_field = rawget(class, k)
if nil == ret_field then
ret_field = r[k]
if 'function' == type(ret_field) then
class[k] = ret_field
else
ins_ret[k] = ret_field
end
end
return ret_field
end,
}) if class.ctor then
class.ctor(ins_ret, ...)
end return ins_ret
end,
}
)
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.PrintMyName
print("MyVector3")
end

  但作者说如果不是 bug,只是为了方便,最后这个不能修改,因为要考虑兼容性,已经有人这么用了,确实是这样,所以我就把这个提交到自己的另一个分支里,在自己的项目使用新方法。

 
 

SLua 中继承 C# 类接口 Slua.Class 的一个 Bug。的更多相关文章

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

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

  2. Java基础知识强化之多线程笔记05:Java中继承thread类 与 实现Runnable接口的区别

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

  3. [转] Java中继承thread类与实现Runnable接口的区别

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

  4. 多线程——Java中继承Thread类与实现Runnable接口的区别

    线程我只写过继承Thread类的,后来知道java多线程有三种方式,今天首先比较一下常用的继承Thread类和实现Runnable接口的区别. 按着Ctrl键进入Thread之后,发现Thread类也 ...

  5. java中继承thread类的其他类的start()方法与run()方法

    java中继承thread或者实现runnable接口的类必须重写run()方法. 如果其执行了start()方法,其实就是启动了线程的run()方法. 注意:如果是实现runnable接口的类是没有 ...

  6. Java中继承,类的高级概念的知识点

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

  7. 使用myeclipse开发java,解决java中继承JFrame类出现The type JFrame is not accessible due to restriction的问题

    在java中创建窗体,导入了java中的JFrame类,之后会出现错误: Access restriction: The type QName is not accessible due to res ...

  8. struts 中继承ActionSupport类

    理论上Struts 2.0的Action无须实现任何接口或继承任何类型,但是,我们为了方便实现Action,大多数情况下都会继承 com.opensymphony.xwork2.ActionSuppo ...

  9. JDBC中重要的类/接口-Connection、DriverManager、ResultSet、Statement及常用方法

    DriverManager(管理一组 JDBC 驱动程序的基本服务) 它的方法: getConnection(String url, String user, String password) 试图建 ...

随机推荐

  1. Java注解知识点摘抄

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  2. 《ext江湖》第8章继承-代码片段

    创建Animal对象 <html> <head> <title>11</title> <META HTTP-EQUIV="Content ...

  3. VIM 及正则表达式

    VIM及正则表达式 一.查找/Search + 统计 1.统计某个关键字 方法是:%s:keyword:&:gn. 其中,keyword是要搜索的关键字,&表示前面匹配的字符串,n表示 ...

  4. Github进行项目管理的常用命令总结

    最近项目中使用github来管理代码,将git的常用命令整理了一下,方便以后查询 查看git的本地配置git config --list 克隆到本地git clone (网页上面的那个http地址 g ...

  5. ASP.NET错误页

    当页面发生错误的时候,ASP.Net会将错误信息展示出来,这样一来不好看,二来会泄露网站的内部实现信息,给网站带来安全隐患,因此需要定制错误页,发生错误时显示开发人员定制的页面404页面放点广告也是好 ...

  6. ES6笔记-字符串方法

    字符串检索方法,indexOf(searchValue,fromIndex)//参数1必需,检索查询的字符串或者值,参数2选题,规定检索的起始位置,不设置默认从0开始 indexOf()方法返回检索字 ...

  7. javascript 事件对象

    1.事件对象  用来记录一些事件发生时的相关信息的对象  A.只有当事件发生的时候才产生,只能在处理函数内部访问  B.处理函数运行结束后自动销毁2.如何获取事件对象  IE: window.even ...

  8. express cookie-session解惑

    express的中间件基于connect模块而来,所以相关文档可以直接参考 http://www.senchalabs.org/connect/ 使用cookie-session中间件过程中,比较困惑 ...

  9. 方便mac os 10.9系统中phpstorm配置php运行环境

    自己安装php,不用mac安装,这样就有php开发环境了. 安装很简单,直接运行一个命令, 需要几分钟,请慢慢等待. curl -s http://php-osx.liip.ch/install.sh ...

  10. 寡人写的第一个HTML5页面

    好吧,其实是抄来的 <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"/> &l ...