C#与XLua通信
XLua 是一个用于在 Unity 游戏开发引擎中绑定 Lua 编程语言的框架,它旨在提供快速、高效的 Lua 和 C# 之间的桥梁。绑定过程经过优化,以最小化性能开销,适用于资源密集型的游戏开发。它提供了简单直观的 API,用于将 C# 函数和对象绑定到 Lua,实现两种语言之间的无缝通信。XLua 设计用于在 Unity 支持的各种平台上工作,包括 Windows、macOS、iOS、Android 等,这使得它成为在不同设备上进行游戏开发的多用途选择。这篇文章就介绍一下 Xlua 和 C# 之间是如何通信的。
Lua
Lua 是一种轻量级、高效的脚本语言,由巴西计算机科学家Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo于1993年创建。Lua 的设计目标是成为一个嵌入式脚本语言,通常用于游戏开发、嵌入式系统、脚本扩展等领域。它具有简单的语法、动态类型、自动内存管理等特性,使其易于学习和使用。
关键特点包括:
轻量级:Lua 的设计使其成为一种轻量级语言,适合嵌入到其他应用程序中。
动态类型:Lua 是一种动态类型语言,变量无需声明类型,可以在运行时改变其类型。
自动内存管理:Lua 具有垃圾回收机制,开发者无需手动管理内存。
可扩展性:Lua 具有强大的扩展性,允许通过C语言编写的模块扩展其功能。
CSharp
C#(CSharp) 是由微软开发的一种面向对象的编程语言。它是 .NET 平台的一部分,旨在提供一种现代、安全、高性能的编程语言。C#最初由Anders Hejlsberg和他的团队在Microsoft于2000年发布,它结合了C++的强大性能和Java的简单性。
关键特点包括:
面向对象:C# 是一种面向对象的编程语言,支持封装、继承和多态等面向对象的概念。
类型安全:C# 是一种静态类型语言,编译时会进行类型检查,有助于减少运行时错误。
直观语法:C# 的语法设计旨在简化开发者的工作,提供直观、清晰的语法结构。
强大的标准库:C# 配有丰富的标准库和 .NET 框架,提供了许多现成的类和功能,使开发更加高效。
跨平台:C# 在近年来通过 .NET Core 和 .NET 5 的推动实现了跨平台支持,使得它不仅限于 Windows 平台。
为什么C#和Lua可以通信
C# 和 Lua之间的相互调用通常是通过桥接工具或中间件实现的。这些工具提供了一种机制,使得 C# 和 Lua 两种语言能够在同一项目中进行交互。其中,XLua是一个典型的桥接工具。
- 绑定框架:工具如 XLua 提供了一个绑定框架,使得 C# 和 Lua 之间的函数调用和数据传递变得更加容易。这些框架允许将 C# 对象和函数绑定到 Lua,以及从 Lua 中调用 C# 的对象和函数。
- 中间层:这些工具创建了一个中间层,将 C# 和 Lua 之间的交互进行了封装。这个中间层负责处理两种语言之间的数据转换、函数调用和其他细节,从而使得开发者可以更方便地在两种语言之间传递信息。
- 类型映射:桥接工具通常提供了一种类型映射机制,使得 C# 的数据类型能够与 Lua 的数据类型进行对应。这种映射是关键,因为 C# 和 Lua 在类型系统上有一些差异,比如 C# 是强类型语言,而 Lua 是动态类型语言。
- Lua 的可扩展性:Lua 本身是一种高度可扩展的语言,可以通过 C 语言编写的模块进行扩展。这使得在 Lua 中调用 C# 函数和对象变得更加容易,因为可以通过 C 编写中间层,实现 C# 和 Lua 之间的交互。
- Lua 的嵌入性:Lua是一种嵌入式语言,这意味着它可以被轻松地嵌入到其他应用程序中。Lua 解释器是一个相对轻量级、独立的库,可以很容易地集成到 C# 等宿主应用程序中,允许在 C# 代码中调用 Lua 脚本。Lua 提供了用于在 C 类代码中注册函数和数据结构的 API,使得 C# 可以调用通过 Lua 编写的函数。
- C# 的托管调用:C# 是一种托管语言,运行在.NET运行时中。在 C# 中,可以通过 P/Invoke(Platform Invocation Services) 或使用专门的库(如NLua、LuaInterface等)来调用本地(非托管)代码,这就为 C# 与 C 语言进行交互提供了便利的手段,而 Lua 正是通过这种方式在 C# 中被调用。
C#和Lua交互方式
C#调用Lua:
- 一般方式:在通常的情况下,C# 调用 Lua 是通过调用 Lua 解释器的底层 DLL 库来实现的。这种方式涉及到与 Lua 解释器的交互,通常使用 Lua 的 CLR package 或其他插件提供的机制。
- XLua方式:在 XLua 中,是使用 C# 调用 XLua 库的接口来实现 C# 与 Lua 的交互,而不是通过 DLL 方式。
- 原理:在 C# 中调用 Lua 的过程涉及到虚拟机的概念,其中 Lua 使用了 Lua 虚拟机来执行 Lua 脚本。虚拟机提供了一个虚拟栈,C# 代码可以通过虚拟栈与 Lua 脚本进行交互。这个栈有两种计数方式,栈底是1,往上递增;栈顶是-1,往下递减;C# Call Lua 时,C# 把请求或数据放在栈顶,然后 lua 从栈顶取出数据,在 lua 中做出相应处理(查询,改变),然后把处理结果放回栈顶,最后 C# 再从栈顶取出 lua 处理完的数据,完成数据交互。
Lua调用C#:
- 一般方式:在通常的情况下,Lua 调用 C# 需要使用 Lua 的 CLR package 或其他 Lua 插件提供的机制。这包括将 C# 对象注册到 Lua 环境中,以便 Lua 可以直接调用 C# 对象的方法。
- XLua 方式:在 XLua 中,有两种主要的方式来实现 Lua 调用 C#:
- Wrap 方式:在 XLua 中,通过生成 Wrap 文件,该文件包含对 C# 类和方法的封装。Lua 文件调用 Wrap 文件,然后 Wrap 文件再调用 C# 文件。这种方式需要在运行前生成 Wrap 文件,但它提供了更高的性能和类型安全。
- 反射方式:当处理系统 API、DLL 或第三方库等情况时,可能无法提前生成静态映射关系。在这种情况下,可以通过动态反射调用 C# 方法的方式来实现 Lua 对 C# 的调用。这种方式灵活,但执行效率相对较低。
XLua简介
xlua github下载地址
XLua 是一个用于在 C# 和 Lua 之间进行交互的库,它提供了一种强大而灵活的方式,使 C# 和 Lua 能够无缝地通信。它提供了一种高效的方式,让开发者能够使用 Lua 脚本来扩展和定制 Unity 应用程序。以下是 XLua 的一些主要特点和功能:
高性能:XLua 采用了一系列优化策略,包括 IL 运行时代码生成,JIT 编译等,以保证在运行时的性能表现。这使得 XLua 适用于对性能要求较高的游戏开发场景。
自动化 Wrap 生成:XLua 能够自动生成 C# 和 Lua 之间的 Wrap 代码,无需手动编写繁琐的映射代码。通过使用自动生成的 Wrap 文件,XLua 能够实现对 C# 类和方法的直接调用。
无 GC 垃圾回收:XLua 的设计考虑了垃圾回收的性能问题,并尽量避免了不必要的 GC 开销。这有助于在运行时减少垃圾回收的频率,提高性能。
支持 Unity 热更新:XLua 能够实现对 Lua 脚本的热更新,这意味着你可以在不重新编译和重新打包应用的情况下,动态更新游戏逻辑。这对于快速迭代和修复问题非常有用。
支持 Unity 事件和协程:XLua 能够直接处理 Unity 的事件系统(比如 MonoBehaviour 的 Awake、Update等方法)和协程,使得 Lua 脚本能够更方便地与 Unity 引擎进行交互。
支持异步调用:XLua 支持异步调用,能够更好地处理异步操作,比如 Unity 的异步加载场景、异步下载等。
丰富的扩展功能:XLua 提供了一系列的扩展功能,包括导出自定义类、委托、泛型支持等,使得XLua更加灵活和易用。
总体来说,XLua 为 Unity 开发者提供了一种灵活、高性能的 Lua 脚本集成方式,让开发者能够在 Unity 中更加方便地利用 Lua 进行快速开发和定制。
XLua关键类
LuaEnv
LuaEnv 是 XLua 中的一个核心类,用于管理 Lua 环境和 Lua 执行引擎,负责管理整个 Lua 环境的创建、执行和资源释放。它充当了 Lua 解释器和执行环境的角色。在 Unity 中,它通常在游戏启动时被创建,用于初始化 Lua 环境,执行 Lua 启动脚本,管理 Lua 全局变量、以及处理 Lua 脚本的热更新等操作。
创建 Lua 环境:在使用 XLua 时,通常需要创建一个 LuaEnv 的实例。这个实例负责管理整个 Lua 环境,包括加载和执行 Lua 脚本。
1
2
3using XLua;
LuaEnv luaEnv = new LuaEnv();执行 Lua 代码:LuaEnv 可以执行 Lua 代码字符串或 Lua 文件。
1
luaEnv.DoString("print('Hello from Lua!')");
注册 C# 对象到 Lua 环境:通过 Global 属性可以注册 C# 对象到 Lua 环境中,使得 Lua 脚本可以直接访问和调用这些 C# 对象。
1
luaEnv.Global.Set("myCSharpObject", new MyCSharpClass());
设置全局变量:
1
luaEnv.Global:Set("myGlobalVar", 42)
获取全局变量:
1
local value = luaEnv.Global:Get<number>("myGlobalVar")
执行 Lua 函数:LuaEnv 通过 DoString 或 DoFile 执行 Lua 代码,也可以通过 Get 方法获取 Lua 函数并执行。
1
2LuaFunction myLuaFunction = luaEnv.Global.Get<LuaFunction>("myLuaFunction");
myLuaFunction.Call();资源释放:在使用完 LuaEnv 后,需要确保及时释放资源,避免内存泄漏。
1
luaEnv.Dispose();
自定义加载器:AddLoader 允许注册自定义的 Lua 脚本加载器,这样就可以在 Lua 脚本中使用自定义的加载逻辑。这对于管理和加载不同文件夹中的 Lua 文件或使用特殊加载逻辑的场景非常有用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 创建 LuaEnv 实例
LuaEnv luaEnv = new LuaEnv();
// 添加自定义加载器
luaEnv.AddLoader((ref string filename) =>
{
// 自定义加载逻辑,返回 Lua 文件内容的字节数组
byte[] luaContent = // ... your loading logic here ...
return luaContent;
});
// 执行 Lua 脚本
luaEnv.DoString("require 'myCustomScript'");
// 释放 LuaEnv 资源
luaEnv.Dispose();在这个例子中,AddLoader 接受一个函数,该函数用于定义自定义加载逻辑。可以在这个函数中根据传入的文件名加载相应的 Lua 文件内容,然后返回该内容的字节数组。
创建一个新的 Lua 用户数据:NewUserData 方法用于在 Lua 环境中创建一个新的 Lua 用户数据。用户数据是一种特殊类型,允许将 C# 对象传递给 Lua,并在 Lua 中使用它。
1
2
3
4
5
6
7
8
9
10
11// 创建 LuaEnv 实例
LuaEnv luaEnv = new LuaEnv();
// 创建一个新的 Lua 用户数据
object myUserData = luaEnv.NewUserData(new MyClass());
// 在 Lua 脚本中使用该用户数据
luaEnv.DoString("print(myUserData.myProperty)");
// 释放 LuaEnv 资源
luaEnv.Dispose();在这个例子中,NewUserData 创建了一个包装了 MyClass 对象的 Lua 用户数据。在 Lua 脚本中,可以通过 myUserData 访问该对象的属性和方法。
LuaFunction
LuaFunction 是 XLua 中用于表示 Lua 函数的类型。通过 LuaFunction,可以在 C# 中调用 Lua 中定义的函数。
获取 LuaFunction 对象:可以通过 LuaEnv 或者从 LuaTable 中获取 Lua 函数。通常,这个对象会被保存在 C# 中以便后续调用。
1
2
3
4
5
6
7luaEnv.DoString(@"
function myLuaFunction(message)
print('Lua method called with message: ' .. message)
end
");
// 获取名为 "myLuaFunction" 的 Lua 函数
LuaFunction myLuaFunction = luaEnv.Global.Get<LuaFunction>("myLuaFunction");调用 Lua 函数: 一旦获取了 LuaFunction 对象,你可以使用 Call 方法来调用 Lua 函数,也可以传递参数给 Lua 函数。
1
2
3
4
5// 调用 Lua 函数
myLuaFunction.Call();
// 传递参数给 Lua 函数
myLuaFunction.Call("Hello"); //Hello获取返回值:如果 Lua 函数有返回值,可以通过 Call 的返回值来获取。
1
2// 调用 Lua 函数并获取返回值
var result = myLuaFunction.Call();错误处理:调用 Lua 函数时,应该注意处理可能发生的错误。可以通过检查 LuaEnv 的 LastException 属性来获取最后一次执行时产生的异常信息。
1
2
3
4
5
6
7
8try
{
myLuaFunction.Call();
}
catch (Exception e)
{
Debug.LogError($"Lua function call error: {e.Message}");
}释放资源:在不再需要 LuaFunction 时,应该及时释放资源,以防止内存泄漏。
1
myLuaFunction.Dispose();
总体来说,LuaFunction 提供了在 C# 中调用 Lua 函数的接口。在与 Lua 交互的过程中,可能会频繁使用这个类型来执行 Lua 中的函数,并根据需要处理返回值和错误。
LuaTable
LuaTable 是在 XLua 中用于在 C# 中表示 Lua 表(table)的类型。Lua 表是一种类似于数组或字典的数据结构,可在 Lua 中用于存储和组织数据。在 C# 中,LuaTable 类型允许在 C# 代码中操作 Lua 表。
创建新表:NewTable 方法用于在 Lua 环境中创建一个新的 Lua 表。这是一种存储数据的数据结构,类似于数组或字典。
1
2
3
4
5// 创建 LuaEnv 实例
LuaEnv luaEnv = new LuaEnv();
// 创建一个新的 Lua 表
LuaTable myTable = luaEnv.NewTable();向 Lua 表中添加元素:使用 Set 方法向表中添加元素。这里的键可以是字符串或整数,值可以是任意类型。
1
2
3
4// 向表中添加数据
myTable.Set("key1", "value1");
myTable.Set("key2", 42);
myTable.Set(1, "item1");将 Lua 表注册到 Lua 环境中:在默认情况下,DoString 方法执行的 Lua 代码与 C# 中的变量并不共享。要在 Lua 脚本中使用 C# 中的变量,需要将这些变量传递到 Lua 环境中。
1
2
3
4
5
6
7// 将 Lua 表注册到 Lua 环境中
luaEnv.Global.Set("myTable", myTable);
// 在 Lua 脚本中使用该表
luaEnv.DoString(@"
print(myTable.key1)
print(myTable.key2)
");从 Lua 表中获取元素:使用 Get 方法从表中获取元素。根据键的类型,可以返回不同类型的值。
1
2
3
4string value1 = myTable.Get<string>("key1");
int value2 = myTable.Get<int>("key2");
string item1 = myTable.Get<string>(1);
Debug.Log(value1); //输出value1迭代LuaTable:使用 ForEach 方法可以迭代 Lua 表中的元素。注意在 XLua 中,LuaTable.ForEach() 方法的类型参数不是可以自动推断的,因此在使用时需要显式指定类型参数,只会迭代 key 为当前指定类型的表元素。
1
2
3
4
5
6myTable.ForEach<string, object>((key, value) => {
Debug.Log($"string Key: {key}, Value: {value}"); // value1 42
});
myTable.ForEach<int, object>((key, value) => {
Debug.Log($"int Key: {key}, Value: {value}"); // item1
});
C# 和 Lua 相互调用
在 Lua 和 C# 之间进行调用通常有两个主要的方向:Lua 调用 C#(Lua Call C#)和 C# 调用 Lua(C# Call Lua)。
C# 调用 Lua 脚本的三种方法
直接运行代码:
1
2LuaEnv luaEnv = new LuaEnv();
luaEnv.DoString("print('Hello from Lua!')");运行Resources目录下的 lua 脚本
1
2
3
4// xlua默认的脚本寻找路径是在Resources文件夹中,但是由于unity并不能识别Lua文件,所以直接加载时会报错的。解决方案是将lua后缀多加一个txt,将其转换为文本文件
LuaEnv luaEnv = new LuaEnv();
// 加载 Resources 目录下的 Lua 脚本文本
luaEnv.DoString("require('LuaTest')");运行任意目录下的lua脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33void Start()
{
LuaEnv luaEnv = new LuaEnv();
//加载脚本loader
luaEnv.AddLoader(MyCustomLoader);
// 相对路径或绝对路径,这里示例使用相对路径
string luaScriptPath = "Assets/Scripts/LuaTest";
try
{
// 执行 Lua 脚本
luaEnv.DoString(string.Format("require '{0}'", luaScriptPath));
}
catch (System.Exception e)
{
Debug.LogError("Lua Error: " + e.Message);
}
}
// 自定义 Lua 文件加载器
byte[] MyCustomLoader(ref string filepath)
{
// 构建完整路径
string fullFilePath = Path.Combine(Application.dataPath, filepath + ".lua");
// 读取 Lua 脚本内容
if (File.Exists(fullFilePath))
{
return File.ReadAllBytes(fullFilePath);
}
return null;
}
C# 调用 Lua 示例
C#访问lua基本数据类型
1 | -- Lua 脚本 |
1 | // C#脚本 |
这些简单类型是值拷贝,即使给新的变量赋值,比如 i = 13,也不会影响到 lua 文件里的变量;除非用 set 方法,才会改变 lua 文件里的简单类型变量,例如:
1 | luaEnv.Global.Set("testNumber", 13); |
C#访问lua function
映射到delegate(推荐方法)
在 XLua 中,将 Lua 函数映射到 C# 的委托类型(delegate)是一种性能较好、类型安全的方式。缺点是需要生成代码(如果没生成代码会抛 InvalidCastException 异常)。
- delegate 怎样声明:对于 function 的每个参数就声明一个输入类型的参数。
- 多返回值要怎么处理:从左往右映射到 c# 的输出参数,输出参数包括返回值,out 参数,ref 参数。
- 参数、返回值支持哪些类型:支持各种复杂类型,out,ref 修饰的,甚至可以返回另外一个delegate。
1 | -- lua表方法 |
1 | // 映射到C#委托 |
映射到LuaFunction
这种方式的优缺点刚好和第一种相反。使用起来很简单,LuaFunction 有一个变参的 Call 函数,可以传任意类型,任意个数的参数,返回值是 object 的数组,对应于 lua 的多返回值。
1 | LuaFunction luaFunction = luaEnv.Global.Get<LuaFunction>("FuncWithMulReturnAndParams"); |
C#访问table
映射到class或者struct
table 的属性可以多于或者少于 class 的属性,可以嵌套其它复杂类型,过程是值拷贝(深拷贝),改变一端的值不会改变另外一端。直接将 Lua 表中的函数映射到 C# 类的字段或属性上是有限制的。通常,这种映射的能力比较有限,而且 XLua 不直接支持在 C# 中定义 Lua 函数。
1 | -- 定义lua全局表 |
1 | // C#映射类 |
1 | //C#执行脚本 |
映射到一个interface(引用方式)
使用代码生成器生成接口的实例是推荐的方式。这种方式依赖于生成代码(如果没生成代码会抛 InvalidCastException 异常),代码生成器会生成这个 interface 的实例,如果 get 一个属性,生成代码会 get 对应的 table 字段,如果 set 属性也会设置对应的字段。甚至可以通过 interface 的方法访问 lua 的函数。这样的方式可以在一定程度上提高代码的类型安全性,并减少手动编写映射代码的工作。在 XLua 中,可以使用工具来生成代码以实现接口映射。
1 | -- lua表 |
1 | // C#接口,必须打上[CSharpCallLua]标签 |
注意,定义接口以后需要执行 XLua-Generate Code。
1 | //浅拷贝,引用方式,在C#中可以修改lua表的值 |
映射到Dictionary<>,List<>
一种更轻量级的值拷贝方式,一般只用于映射静态数据。对于表中的键值对元素,可以映射到字典,对于表中的列表元素,可以映射到列表。由于映射到字典或列表时,value 的值的类型不一定相同,所以可以使用 object 类型。
1 | -- lua表数据 |
1 | // C#映射到字典 |
如果将字典的键类型设置为某一种数据类型,例如 int,那么只会返回表中键类型为 int 的字典。
1 | // C#映射到列表 |
映射到LuaTable类
一种更轻量级的引用方式,映射到 LuaTable 类,一般不推荐使用。这种方式好处是不需要生成代码,但也有一些潜在的问题和限制,比如效率低,比方式2要慢一个数量级,没有类型检查等。而且如果 LuaTable 不用了需要释放 Dispose ,不然会产生垃圾。这种方式更适合用在一些比较复杂且使用频率很低的情况。这种方式是浅拷贝的。
1 | -- lua表数据 |
1 | // 映射到LuaTable |
使用建议
限制全局访问:访问 Lua 全局数据的代价相对较高,尤其是对 table 和 function。建议在初始化时将要调用的 Lua function 获取一次(映射到 delegate),然后保存下来,以后直接调用该 delegate,以减小性能开销。
解耦与 Lua 的关系:如果 Lua 侧的实现部分都以 delegate 和 interface 的方式提供,使用方可以完全与 XLua 解耦。可以有一个专门的模块负责 XLua 的初始化以及 delegate、interface 的映射,然后将这些 delegate 和 interface 设置到需要使用它们的地方。
管理 LuaFunction 的生命周期:如果你使用 LuaFunction,要注意管理它们的生命周期。确保在不再需要时适当地进行清理,以防止内存泄漏。
避免频繁的 Lua 与 C# 交互:避免在短时间内频繁进行 Lua 与 C# 的交互,因为这可能会导致性能问题。尽量将交互的操作集中在一起,减少交互的次数。
Lua 调用 C# 示例
创建和访问 C# 实例
1 | // C# 类 |
1 | -- 创建 C# 对象实例 |
或者
1 | local newGameObj = CS.UnityEngine.GameObject("Empty") |
需要注意的点是:
lua里没有new关键字;
C#相关代码都放在CS下;
lua支持重载;
访问 C# 静态方法/属性
1 | public class MyCSharpClass |
1 | -- 获取 C# 类型信息 |
lua 重载
直接通过不同参数类型进行重载,示例:
1 | public class MyCSharpClass |
1 | -- TestFunc 被重载了,xLua 需要明确传递参数类型 |
但需要注意的是, xlua 只一定程度上支持重载函数的调用,因为 lua 的类型远远不如 C# 丰富,存在一对多的情况,比如 C# 的 int,float,double 都对应于 lua 的 number,如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码中排前面的那个)。
可变参数方法
1 | public class MyCSharpClass |
1 | -- lua端调用示例 |
使用Extension methods
在 C# 里定义了,lua 里就能直接使用。
注:使用 Extension methods 可以在已有的类型(types)中添加方法(Methods),而无需通过增加一种新的类型或修改已有的类型。比如说,想要给 string 类型增加一个 PrintStrLength() 方法,打印出字符串的长度。使用 Extension methods 可以不需要去修改 string 类型而实现这一方法。
1 | [ ] |
1 | -- lua端调用拓展方法 |
注意:
- Extension methods 扩展方法必须定义在一个静态类中
- Extension methods 是一种特殊的静态方法,在被扩展的类型调用时要像实例方法一样调用:
"hello".PrintStrLength();
- 扩展方法的声明:使用 this 关键字,后面跟着要操作的对象类型
- 扩展方法的使用:使用扩展方法时需先使用 using 导入扩展方法所在的名字空间
- C#不能扩展静态类(如 System.Convert)
泛化(模版)方法
不直接支持,可以通过 Extension methods 功能进行封装后调用。
使用泛型为参数的方法
1
2
3
4
5
6
7
8
9// C#
public class MyCSharpClass
{
//定义一个具有泛型为参数的方法
public void Method(List<string> strArray)
{
Debug.Log(string.Join(" ", strArray));
}
}1
2
3
4-- lua
myClass = CS.MyCSharpClass()
myTable = {"lua", "C#", "C++"}
myClass:Method(myTable) -- lua C# C++调用C#中的泛型方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// C#
[ ]
public class MyClass
{
public void Print<T>(T value)
{
Debug.Log(value);
}
}
[ ]
public static class MyClassExtensions
{
public static void LuaPrint(this MyClass myClass, int value)
{
myClass.Print(value);
}
public static void LuaPrint(this MyClass myClass, string value)
{
myClass.Print(value);
}
}1
2
3
4-- lua
myClass = CS.MyClass()
myClass:LuaPrint(42) --42
myClass:LuaPrint("Hello") --Hello
枚举类型
1 | public enum Language |
1 | -- lua端访问 |
delegate使用(调用,+,-)
使用Lua代码访问 C# 委托时需要注意,访问委托类型的方式与访问静态变量的方式相同,访问(静态/非静态)委托的变量的方式与访问(静态/非静态)成员变量的方式相同。由于在 Lua 中没有 “+=” 和 “-=” 操作符,在改变委托链的时候只能使用”+“和”-“操作符。
+
操作符:对应 C# 的 + 操作符,把两个调用串成一个调用链,右操作数可以是同类型的 C# delegate 或者是 lua 函数。
-
操作符:对应 C# 的 - 操作符,把一个 delegate 从调用链中移除。
注:delegate 属性可以用一个 LuaFunction 来赋值。
1 | public class DelegateExample : MonoBehaviour |
1 | -- lua端调用 |
event
1 | public class EventClass |
1 | local eventClass = CS.EventClass() |
复杂类型
对于一个有无参构造函数的 C# 复杂类型,在 lua 侧可以直接用一个 table 来代替,该 table 对应复杂类型的 public 字段有相应字段即可,支持函数参数传递,属性赋值等,例如:
1 | // C# |
1 | -- lua |