Lua 基础语法(六)xLua Lua 调用 C# 交互

核心知识点:Lua 访问 C# 类 / 对象 / 组件、cs 命名空间前缀、对象创建、GameObject 查找获取组件、属性读写、重载函数坑点、可变参数、委托与多播委托、事件注册移除、桥接 (Gen) 文件、虚拟栈交互底层原理

一、Lua 访问 C# 基础规则

1 概念

Lua 脚本访问 C# 的类、静态成员、实例对象,必须加cs.前缀;对应 C# 完整命名空间;Lua 没有new关键字,直接调用类名()创建 C# 对象实例。

2 特性 & 规则

  1. 访问格式:cs.命名空间.类名;如cs.UnityEngine.GameObject

  2. 创建 C# 对象:local obj = cs.UnityEngine.GameObject(),不用 new;

  3. 调用实例成员方法冒号:,会自动隐式传入 self(对象实例);

  4. 调用静态成员方法 用点.,不需要 self;

  5. 获取组件、Transform、Time.timeScale 这类属性可以直接读写。

3 语法 & 代码片段

复制代码
--  创建GameObject
local go = cs.UnityEngine.GameObject("测试物体")
--  查找物体
local cube = cs.UnityEngine.GameObject.Find("Cube")
--  获取组件,冒号调用实例方法
local boxColl = cube:GetComponent(cs.UnityEngine.BoxCollider)
--  读写组件属性
boxColl.size = cs.UnityEngine.Vector3(5,1,1)
--  读写静态属性时间缩放
cs.UnityEngine.Time.timeScale = 0.5

4 相关知识点对比

.点号 VS :冒号

静态:.,无 self; 实例方法::,自动把调用对象作为 self 传入第一个参数。

5 模块拓展

Vector3/Quaternion 等结构体同样用 cs. 前缀构造。

二、Lua 访问 C# 类:实例、静态、继承

1 概念

Lua 可以创建 C# 类实例;访问实例成员、静态成员;子类对象可以直接访问继承自父类的成员,遵循 C# 继承规则。

2 特性 & 规则

  1. 实例对象:local obj = cs.命名空间.类名()

  2. 实例成员(字段 / 属性 / 实例函数):冒号调用;

  3. 静态成员(static):类名.直接访问,不需要实例;

  4. 子类实例可以直接访问父类公开成员,和 C# 行为保持一致。

3 语法 & 代码片段

复制代码
--  创建子类实例
local testObj = cs.MyNamespace.ChildClass()
--  读写实例属性
testObj.dmf = 123
--  调用实例方法
testObj:DMFFunc()
--  访问继承自父类的成员
testObj.bmf = 2048
testObj:BMFFunc()
--  访问静态成员,点号
cs.MyNamespace.BaseClass.bsf = 4096

4 相关知识点对比

实例访问 VS 静态访问

实例:必须先 new 出对象,用:调用方法; static 静态:直接类名.访问,不用创建对象。

5 模块拓展:结构体 struct 在 Lua 中等价用 table 传参。

三、C# 多返回值函数在 Lua 中调用(ref/out 参数)

1 概念

C# 函数通过ref传入、out输出实现多返回;Lua 调用时,结构体参数直接传 table;ref 需要传入初始值;out 不需要传入初始值。

2 特性 & 规则

  1. ref:实参必须传入初始值;函数内可以读取 + 修改;

  2. out:不需要传入初始值;函数执行完成向外输出数据;

  3. C# 的 struct 结构体,Lua 传参直接传 table;

  4. 函数多个 out/ref,Lua 接收多个返回值。

3 语法 & 代码片段

复制代码
--  调用C#多返回函数,struct用table传参,ref传初始值
local a,b,c,d = testObj:MultiFunc(
    {x=3,y="ABC"}, --结构体对应table
    100, --ref参数(需要初始值)
    nil, --out参数不需要初始输入
    function(s) print("Lua回调:",s) end --Action委托回调
)
print(a,b,c,d)

4 相关知识点对比

ref VS out

ref:进入函数可读可改,调用方必须传初始数据; out:只负责向外输出,调用不需要传入有效值。

5 模块拓展:C# 返回委托,Lua 可以接收并调用。

四、重载函数调用坑点(int /float)

1 概念

Lua 内部数字全部为 double (number),调用 C# 重载函数时无法区分 int、float、double;匹配顺序取决于 C# 桥接生成时方法注册顺序,先注册的重载优先被执行。

2 特性 & 规则

  1. Lua 只有一种 number,没有区分整数、单精度、双精度;

  2. 同名重载,如果一个 int 一个 float,传数字时优先匹配靠前注册的那一个;

  3. 业务规避方案:尽量避免 C# 写仅数值类型区分的重载;或者封装不同名字辅助函数。

3 相关知识点对比

C# 编译期重载匹配 VS xLua 运行时匹配

C# 编译:编译阶段根据实参类型精确匹配; Lua:运行时,全部 number,按桥接注册先后顺序匹配。

5 模块拓展:字符串、布尔、对象类型重载可以正常区分。

五、可变参数 params 数组函数调用

1 概念

C# params可变参数数组,Lua 调用直接依次传入多个实参,xLua 内部自动包装成数组。

2 特性 & 规则

  1. C# params T[]可变数组;Lua 直接写多个参数;

  2. 内部 xLua 自动把多个实参封装为 C# 数组传入。

3 语法 & 代码片段

复制代码
--C#: void TestParams(int a, params string[] args)
testObj:TestParams(123,"hello","world","test")

4 相关知识点对比

C# params 数组 VS Lua ... 可变参数

C# params:C# 侧定义数组;Lua 直接平铺传参; Lua ...:Lua 脚本原生可变参数。

5 模块拓展:params 支持引用类型对象数组。

六、委托 Delegate、事件 Event Lua 注册 / 移除

1 概念

Lua 中可以构造匿名函数,去订阅 C# 委托、事件;实现 Lua 逻辑订阅 C# 侧的回调。

2 特性 & 规则

  1. Lua 没有+=-=运算符;委托多播使用= 原委托 + 新委托;移除用= 原委托 待移除委托

  2. 事件 Event:外部只能+/-=订阅取消,不能直接赋值;xLua 支持 Lua 匿名函数注册事件;

  3. 多播委托可以挂载多个 Lua 函数,全部会被执行。

3 语法 & 代码片段

复制代码
--委托多播
testObj.testDelegate = testObj.testDelegate + function(s)
    print("Lua委托回调",s)
end
​
--事件注册
testObj:Add_TestEvent(function()
    print("Lua事件触发")
end)
​
--事件移除
testObj:Remove_TestEvent(function()
    print("Lua事件触发")
end)
​
--C#内部调用触发事件
testObj:CallEvent()

注意:xLua 会自动生成 Add_XXX / Remove_XXX 包装方法供 Lua 访问事件。

4 相关知识点对比

普通委托字段 VS event 事件

委托字段:可以直接覆盖赋值; event 事件:禁止外部直接赋值,只能加 / 减订阅。

5 模块拓展:回调注意对象生命周期,防止委托内存泄漏。

七、xLua 底层交互原理:虚拟栈 + 桥接 Gen 文件

1 概念

Lua 与 C# 并不直接互相调用;中间依靠虚拟栈做数据中转站,配合 Generate Code 生成的桥接(Gen)文件完成跨语言交互。

2 特性 & 规则

  1. 虚拟栈:

    任意一方要传递数据,先压入虚拟栈; 另一方从栈顶读取数据;所有参数、返回值全部经过栈中转; 不是直接内存访问,会带来少量性能开销。

  2. Gen 桥接文件:XLua 菜单

    复制代码
    Generate Code

    生成,存放在 Gen 文件夹;

    标记[CSharpCallLua] / [LuaCallCSharp]的类、接口、委托,会生成对应的桥接代码; Lua 不能直接调用 C#;通过桥接代码再去调用原生 C#; 修改 C# 被标记的类 / 委托,必须重新 Generate Code、Inject 注入,否则报错。

  3. 整体调用链路:

    C# 调用 Lua:C# → xLua C++ DLL → 虚拟栈 → Lua 脚本 Lua 调用 C#:Lua → Gen 桥接代码 → 虚拟栈 → C# 业务代码

4 相关知识点对比

无桥接 VS Gen 桥接

没有桥接:Lua 无法识别 C# 自定义类型; Gen 桥接:生成适配层,打通两边类型系统。

5 模块拓展:面试高频考点,热更新笔试常考虚拟栈原理。

综合拓展

1 业务流程 Lua 使用 cs. 前缀访问 Unity/C# → 创建对象、Find 查找、GetComponent 获取组件、读写属性;冒号调用实例方法、点号调用静态;调用 ref/out 多返回函数;注意 number 无法区分 int/float 重载坑;调用 params 可变参数;Lua 匿名函数订阅 C# 委托事件;理解虚拟栈 + Gen 桥接底层交互。

2 高频 Bug 排查清单

  1. Lua 访问 C# 类报错:忘记写cs.命名空间前缀;

  2. 实例方法调用报错:实例方法误用.,应当使用:冒号;

  3. 重载函数调用结果不符合预期:Lua number 不区分 int/float,受桥接注册顺序影响;

  4. 调用委托 / 事件报错:C# 类型没有打上标签,没有执行 Generate Code 生成桥接;

  5. 修改 C# 标记类之后行为异常:修改类型没有重新 Generate Code+Inject 注入。

3 核心考点 cs 命名空间前缀;.:调用区分;Lua 创建 C# 对象;ref/out 多返回值调用;重载函数 int/float 的坑;params 可变参数;Lua 订阅 C# 委托事件;xLua 虚拟栈原理;Gen 桥接文件作用、Generate Code 时机。

4 进阶拓展学习方向 xLua Hotfix 热补丁;AB 包下载 Lua 脚本 + AddLoader 完整热更新 Demo;内存泄漏排查(委托、LuaEnv 释放)。

相关推荐
Joolun商城源码_Java1 小时前
Java 商城技术栈怎么选:单体、微服务还是前后端分离多端?
java·开发语言·微服务
charlie1145141911 小时前
浅谈C++的列表初始化std::initializer_list
开发语言·数据结构·c++·list·开源项目
拒绝内耗。1 小时前
一个请求进入 Java 应用后,怎么找到我们写的方法?
java·开发语言
励志不掉头发的内向程序员1 小时前
【LibreCAD 2D架构】RS_Line创建之后发生了什么?从图形容器到屏幕渲染
开发语言·c++·qt·学习·系统架构
夕除1 小时前
redis--013
java·开发语言
object not found1 小时前
Nuxt4去掉body中默认的边距
开发语言·后端·rust
Java后端的Ai之路1 小时前
Git pull弹出vim编辑器完整排查指南
开发语言·人工智能·git·编辑器·vim
戌中横3 小时前
数据持久化——PlayerPrefs
c#·数据持久化
2501_941875287 小时前
从配置中心到动态管理的互联网工程语法演进与多语言实践分享
开发语言·python