SLua 中继承 C# 类接口 Slua.Class 的一个 Bug。
由于目前要把大量的代码移植到 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。
同时,我做了如下的一些小修改:
- 可以直接使用派生类调用积累的静态成员方法,如基类 Base.ShowStatic(),那么派生类可以直接使用:Derived.ShowStatic();
- 增加了一个名为 ctor 的可选构造函数(这个借鉴了云风给出的 lua-oop 方案);
- 保持通过访问父类方法使用 __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。的更多相关文章
- Java中继承thread类与实现Runnable接口的区别
Java中线程的创建有两种方式: 1. 通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2. 通过实现Runnable接口,实例化Thread类 在实际应用中, ...
- Java基础知识强化之多线程笔记05:Java中继承thread类 与 实现Runnable接口的区别
1. Java中线程的创建有两种方式: (1)通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中. (2)通过实现Runnable接口,实例化Thread类. 2. ...
- [转] Java中继承thread类与实现Runnable接口的区别
Java中线程的创建有两种方式: 1. 通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2. 通过实现Runnable接口,实例化Thread类 在实际应用中, ...
- 多线程——Java中继承Thread类与实现Runnable接口的区别
线程我只写过继承Thread类的,后来知道java多线程有三种方式,今天首先比较一下常用的继承Thread类和实现Runnable接口的区别. 按着Ctrl键进入Thread之后,发现Thread类也 ...
- java中继承thread类的其他类的start()方法与run()方法
java中继承thread或者实现runnable接口的类必须重写run()方法. 如果其执行了start()方法,其实就是启动了线程的run()方法. 注意:如果是实现runnable接口的类是没有 ...
- Java中继承,类的高级概念的知识点
1. 继承含义 在面向对象编程中,可以通过扩展一个已有的类,并继承该类的属性和行为,来创建一个新的类,这种方式称为继承(inheritance). 2. 继承的优点 A.代码的可重用性 B.子类可以扩 ...
- 使用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 ...
- struts 中继承ActionSupport类
理论上Struts 2.0的Action无须实现任何接口或继承任何类型,但是,我们为了方便实现Action,大多数情况下都会继承 com.opensymphony.xwork2.ActionSuppo ...
- JDBC中重要的类/接口-Connection、DriverManager、ResultSet、Statement及常用方法
DriverManager(管理一组 JDBC 驱动程序的基本服务) 它的方法: getConnection(String url, String user, String password) 试图建 ...
随机推荐
- C语言程序设计概述
1 概论 1972年Dennis Ritchie发明了C语言,而后Dennis Ritchie又使用C语言重写了Unix系统,自那以后C语言逐渐受到了全世界大多数编程爱好者的喜爱,后期的主流操作系统L ...
- 下载youku视频(python3)
https://github.com/chenfengyuan/download-youku-video 用tornado写的下载脚本, 从flvcd.com得到下载地址. 因为我这边连youku的速 ...
- nginx 代理 proxy_pass设置
#img.test.com/img1 实际访问的路径是 http://127.0.0.1:123/a1 #img.test.com/img2 实际访问的路径是 http://127.0.0.1:123 ...
- datatable 行列转换
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Javascript自执行匿名函数(function() { })()的原理浅析
匿名函数就是没有函数名的函数.这篇文章主要介绍了Javascript自执行匿名函数(function() { })()的原理浅析的相关资料,需要的朋友可以参考下 函数是JavaScript中最灵活的一 ...
- jsp查询页面和结果页面在同一页面显示和交互
用frameset实现查询页面和结果页面在同一页面 用target实现交互显示在同一页面上 请参照以下方法解决: main.jsp: <html> <head> <met ...
- Toy Storage
Toy Storage 题型与2318 TOYS一样,注意要对线段排序,现在模板又更新了~~ #include<iostream> #include<cstdio> #incl ...
- Windows命令行语法说明
摘自:http://lavasoft.blog.51cto.com/62575/1113234 Windows命令行语法说明 说来惭愧,用windows这么多年了,对其命令行语法看得似懂非懂, ...
- 解决ubuntu侧边栏固定应用单击无反应的问题
Linux下有些绿色软件,不需要安装就可以双击启动,但有些程序在打开后直接在 Launcher 中右键选择 Lock to Launcher ,但是,有时候单击图标后并未启动应用,下面给出解决方法. ...
- C#中引用(ref关键字)参数
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 函数的参 ...