代码仓库:https://github.com/JomiXedYu/LuaSharp
Contents
为什么说适合熟悉C#的Lua开发者
- 使lua拥有面向对象的关键字与方法,关键字、方法名、抽象层基本采用自.Net规则。
可以在这里看到的内容
- 拥有类(class),类型(Type),实例(instance)
- base,重写时需要调用父类的方法。
- 直接在类中写重载运算符。
- 支持gettype(class) / typeof(class) 使用obj:GetType() 方法,每个类型对象唯一
- istype关键字,istype(obj, class),相当于C#的is,istype可判断继承关系。
- List动态数组,Dictionary有序字典,Stack与Queue常用四容器。
- 新增
for item in each(ienumertor) do end
语法,只要是实现了GetEnumerator方法的容器,都可以使用each进行迭代。 - 位运算
- 支持Flag的枚举类
- Convert类与int.Parse等类型转换(没什么用)
- Delegate委托,支持多播,委托同时可以像C#一样支持使用Invoke()或者直接()执行。
- 字符串的处理类,让字符串变量拥有成员方法,如
str:Replace("a", "b")
- 字符串的Format方法,支持{0} {1}形式的字符串格式化
- IO操作,File.WriteAllText 文件读写、复制、删除等操作方法,Path 路径相关操作类
- 字符串构建器,Text.StringBuilder
- Activator
- DateTime Random等通用类型
关于命名
本框架使用Pascal命名规则,核心级函数(关键字)为全小写,私有成员可以使用m_member或者__menmber来提醒其他开发者不要来使用该成员。
用途
- 普通Lua开发
- UnityEngine (分支
- LoveEngine (分支(暂时废弃)
关于智能提示
本框架推荐使用VSCode + EmmyLua插件进行开发,同时对VSCode的代码片段Snippets进行配置,可以达到同比VS的快速开发。
类型与对象
类型的实现结构在最下面
类使用class.extends关键字来继承,一个lua模块对应一个类,基类是可选的,如果不写则继承于Object
{{EJS0}}
为了标识每个类的不同,可以采用路径全名的命名方式来声明。
{{EJS1}}
样例
{{EJS2}}
实例化,(C#的new关键字,放在后面是为了更好的智能提示,如果想更接近语法,还提供了new(TestClass)(params)的方法,但是默认没有智能提示。
{{EJS3}}
支持type对比,(C#的typeof关键字):
gettype为本框架的类型获取,而typeof是在声明前先对typeof进行保存在覆盖声明,使用时会判断参数是否为本框架的类型,如果不是则调用保存的typeof方法,主要应对和别的框架一起使用时可能会造成的冲突,如xLua。(注:如其他含有typeof的框架启动于本框架之后,并没对typeof进行处理的话则会出现错误,xLua是在启动之前)
{{EJS4}}
支持包含基类的类型判断,(C#的is关键字):
{{EJS5}}
支持Equals的重写:
{{EJS6}}
Object静态方法,对比地址:
{{EJS7}}
声明静态类
{{EJS8}}
字符串
可以让字符串变量当做对象来使用,例如
{{EJS9}}
如果直接操作字符串则需要使用静态方法
{{EJS10}}
支持C#中的Format
{{EJS11}}
枚举类型
lua中直接拿表当枚举类型,框架内只是提供了一些处理的方法,如Flag等。
{{EJS12}}
- 按值获取枚举名
{{EJS13}}
- 支持Flag(由位运算“或”驱动)
{{EJS14}}
运算符重载
{{EJS15}}
运算符重载直接在类中实现,可以搭配VSCode配置Snippets代码片段来达到使用VS的快速开发。 重载方法名有:
- operatorAdd +
- operatorSub –
- operatorMul *
- operatorDiv /
- operatorEq ==
- Invoke ()
类型转换
Convert类和对应类型的Parse方法
{{EJS16}}
容器
常用的List和Dictionary实现了GetEnumerator方法,可以直接使用each迭代器,同时也支持lua原版的ipairs与pairs迭代器,并且容器拥有ForEach方法迭代器。
{{EJS17}}
文件处理
- File.ReadAllText(path)
- File.ReadAllLines(path)
- File.WriteAllText(path, content)
- File.WriteAllLines(path, contents)
- File.AppendAllText(path, content)
- File.AppendAllLines(path, contents)
- File.Exist(path)
- File.Copy(path, targetPath)
- File.Move(path, targetPath)
- File.Delete(path)
字符串构建
- StringBuilder类
{{EJS18}}
委托
C#在语言层面实现委托,将实例封装在委托对象中,而Lua使用类似函数指针的方式对其操作,虽然可以用闭包匿名函数的方式来讲self保存起来,但是却必须一直保存着匿名函数的引用,否则在进行多播时会无法移除。
{{EJS19}}
位运算
- And
- Or
- Xor
- Not
- LShift
- RShift
额外的其他东西
- 序列化方法,将lua表转换为字符串,或将字符串反序列化为lua语句/表
- Lua表的拓展功能,数组拷贝、深拷贝等
类型系统的设计
类型系统中最基础的两个东西,Object与Type,Object是面向对象万物起源,Type则是类型根基,Type中储存了许多类型数据:类名、原型、继承关系等。程序中每个class仅有一个Type实例,我将这些Type实例储存到一个叫AppDomain的表中,并用类名字符串作为标识。
继承与静态方法都会生成一个Type,并加入AppDomain中,类名可以使用路径(命名空间)来限定唯一,使用class.extends方法同时会给类添加New方法
在调用New之后,先分配新对象,为新对象添加类型对象指针(为了不干扰类成员所以添加在了元表中),在从本类递归查找至最基类(Object)后,从Object一层一层的调用构造函数(constructor)直到本类型构造完毕。(因为构造时传入了新对象,而类型的成员变量都是在构造函数中进行初始化的,所以新对象拥有基类到本类的所有构造出的成员变量,而方法则是通过虚函数(通过lua的__index实现)的方式访问。
继承关系,使用Lua元表metatable中的__index字段访问虚函数,通过lua的”:”符号,可以默认传入self(this)指针为隐藏参数,所以在类型方法中self实际就是方法调用者,也就是实例对象。
GetType是Object的非虚函数,任何继承于Object的实例对象都可以调用该方法,前面说到,在New时会在实例对象的元表中附加了类型对象指针,直接指向了本类唯一的Type,执行GetType时直接将该指针返回。
typeof则是通过类型元表中的名字,在AppDomain中查找后返回。
istype是包含继承关系的,所以直接调用的Type中的IsInstanceOfType方法。
Activator,Type中保存了类型的原型,可以通过Type来实例化对象,但其实在Lua中直接New就可以了,没什么用。