xLua学习笔记——C#与Lua相互调用(贰)
三、C# 调用 Lua
(一) 前言
对于 Lua 脚本,C# 只需调用一个主入口 Main.lua 即可,其他 lua 脚本均可由 Main.lua 调用,比如我们将代码写到 Test.lua 中,这样Main.lua就可以调用:
print("Main Lua脚本启动")
require("Test")
对于 C# 脚本,我们只需写下类似这样的代码,后续就可以调用 Lua 相关的东西了:
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
// C#调用Lua相关代码...
}
后面篇章我会直接用 Lua 代码、C# 代码 来说明这些核心的代码,而上面这些重复的调用步骤就不提及了
(二)全局变量获取
通过之前 LuaMgr 中的 Global 属性,可以获取到 Lua 的全局变量,而 Global 属性是一张表,在 Xlua 中,Lua 的 table 对应 C# 的 LuaTable 类,里面常用的有 Get 和 Set 两种方法,分别用于获取和修改表的属性,接下来举个例子:
1、Lua 代码如下:
num = 1.2
name = "jack"
sex = true
2、C# 获取全局变量代码如下:
float num = LuaMgr.GetInstance().Global.Get<float>("num");
string name = LuaMgr.GetInstance().Global.Get<string>("name");
bool sex = LuaMgr.GetInstance().Global.Get<bool>("sex");
3、C# 修改全局变量代码如下:
float n = LuaMgr.GetInstance().Global.Get("num"); // 1.2
n = 10; //这样并不会影响Lua中的值
LuaMgr.GetInstance().Global.Set("num", 20);
n = LuaMgr.GetInstance().Global.Get("num"); // 20
获取变量使用 Get 方法即可,而修改变量则不能直接修改变量,因为变量 n 是值拷贝,要调用 Set 方法进行修改
(三)全局函数获取
1、Lua 代码如下:共 4 种测试函数
-- 无参无返回
testFun = function()
print("无参无返回");
end
-- 有参有返回
testFun2 = function(a)
print("有参有返回")
return a
end
-- 多返回值
testFun3 = function()
print("多返回值")
return 1,false,"jack"
end
-- 变长参数
testFun4 = function(...)
print("变长参数")
local arg = {...}
for k,v in pairs(arg) do
print(k,v)
end
end
C# 获取 Lua 函数的方式有 2 种,一种是委托(自定义、C# 自带的、Unity 自带的均可),另一种是 Xlua 提供的 LuaFunction类,这个类对应 lua 中的 function,使用方式也类似。就像 LuaTable 类对应 Lua 中的 table 一样。
两种方式各有利弊,但主要还是委托的方式。委托接收函数后,直接用委托的括号调用即可,LuaFunction 接收后,调用 Call 方法即可调用
2、C# 获取无参无返回值函数:
// Unity自带委托
UnityAction ua = LuaMgr.GetInstance().Global.Get<UnityAction>("testFun");
ua();
// LuaFunction
LuaFunction lf = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun");
lf.Call();
3、C# 获取有参有返回值函数:
这里我们用自定义委托,但是注意,除了无参无返回值的委托 XLua 可以识别外,其他自定义委托均需要加上 [CSharpCallLua] 这个特性,并且需要重新在 Unity 编辑器中 Generate Code 才可以被识别:
// 有参有返回值委托
[CSharpCallLua]
public delegate int CustomCall2(int a);
LuaFunction 的返回值是一个 object [],我们只需要获取第一个返回值,所以 [0] 即可。
怎么样,这个 LuaFunction 是不是和 lua 中的 function 几乎一模一样,只要按 lua 语法来就会用了
// 自定义委托
CustomCall2 call2 = LuaMgr.GetInstance().Global.Get<CustomCall2>("testFun2");
Debug.Log(call2(10));
// LuaFunction
LuaFunction lf2 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun2");
Debug.Log(lf2.Call(30)[0]);
4、C# 获取多返回值函数:
多返回值的委托定义如下:返回值对应 lua 的 function 的第一个返回值,其余返回值用 out 替代
[CSharpCallLua]
public delegate int CustomCall3(out bool b, out string c);
// 自定义委托
int a; bool b; string c;
CustomCall3 call3 = LuaMgr.GetInstance().Global.Get<CustomCall3>("testFun3");
a = call3(out b, out c);
Debug.Log($"{a}_{b}_{c}");
// LuaFuntion
LuaFunction lf3 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun3");
object[] objs = lf3.Call(1000);
for (int i = 0; i < objs.Length; i++)
Debug.Log(objs[i]);
5、C# 获取变长参数函数:
[CSharpCallLua]
public delegate void CustomCall4(params int[] args); // 变长参数类型根据实际情况而定
// 自定义委托
CustomCall4 call4 = LuaMgr.GetInstance().Global.Get<CustomCall4>("testFun4");
call4(1, 2, 3, 4, 5);
// LuaFuntion
LuaFunction lf4 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun4");
lf4.Call(6, 7, 8, 9, 10);
可以发现,使用 LuaFunction 会比使用委托更加方便,但是因为 object 拆箱装箱会有一些性能开销,因此平时不太推荐使用 LuaFunction,而使用委托会更加好一些。
(四) Table 映射 List 和 Dictionary
我们知道,Lua 中的表可以表示多种多样的类型,比如列表、字典、类等等,下面就先来说一下映射 List 和 Dictionary
1、Lua 代码如下:
-- List
list = {1,2,3,4,5,6}
-- Dictionary
dict = {
["1"] = 1,
["2"] = 2,
["3"] = 3,
["4"] = 4
}
2、C# 获取 List:
和之前的变量一样,无法直接在 C# 中直接修改 Lua 的值。当然你还可以使用 LuaTable 类来装载,这样就可以用 set 方法修改了变量了
// List接收
List<int> list = LuaMgr.GetInstance().Global.Get<List<int>>("list");
for (int i = 0; i < list.Count; i++)
Debug.Log(list[i]);
//list[0] = 100; // 值拷贝,这样修改不会改变lua的值
// LuaTable接收
LuaTable tableList = LuaMgr.GetInstance().Global.Get<LuaTable>("list");
for (int i = 1; i <= tableList.Length; i++) // Lua的table索引默认从1开始
Debug.Log(tableList.Get<int,int>(i));
tableList.Set<int, int>(1, 100); // 修改索引1的值为100
3、C# 获取 Dictionary:
字典也是可以用 LuaTable 装的,凡是 Lua 中为 Table 的都可以装,后面就不演示了,这里直接用 C# 的字典来装
Dictionary<string, int> dict = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("dict");
foreach (string item in dict.Keys)
{
Debug.Log(item + "_" + dict[item]);
}
(五) Table 映射 C# 类
1、Lua 代码如下:
-- Class
MyClass = {
age = 2,
sex = true,
name = "jack",
myFunc = function()
print("Func!")
end,
mychild = {
id = "123"
}
}
2、C# 代码如下:
注意类声明修饰符需要是 public,成员变量可多可少,只不过对不上的话会被忽略
public class MyClass
{
public int age;
public bool sex;
public string name;
public UnityAction myFunc;
public MyChildClass mychild;
}
public class MyChildClass
{
public string id;
}
MyClass obj = LuaMgr.GetInstance().Global.Get<MyClass>("MyClass");
Debug.Log(obj.age);
Debug.Log(obj.sex);
Debug.Log(obj.name);
obj.myFunc();
Debug.Log(obj.mychild.id);
(六) Table 映射 C# 接口
1、Lua 代码如下:
上面类的代码几乎一样,只不过要去掉之前的子类成员
MyClass2 = {
age = 2,
sex = true,
name = "jack",
myFunc = function()
print("Func!")
end
}
2、C# 代码如下:
接口需要加上 [CSharpCallLua] 特性,并且接口内只能声明属性,不能声明成员变量
[CSharpCallLua]
public interface IInterface
{
int age { get; set; }
bool sex { get; set; }
string name { get; set; }
UnityAction myFunc { get; set; }
}
获取并没有什么差别,唯一要注意的就是接口是引用拷贝,这和其他的不同,也就是说你可以直接在 C# 中修改 lua 的变量
IInterface obj = LuaMgr.GetInstance().Global.Get<IInterface>("MyClass2");
Debug.Log(obj.age);
Debug.Log(obj.sex);
Debug.Log(obj.name);
obj.myFunc();
obj.age = 100; // 注意:接口是引用拷贝,即修改接口会影响lua的变量
(七)Table 映射 LuaTable 类
我们之前在映射 List 中讲过了这个类了,不过现在需要再稍微详细讲一下这个类,Lua 代码和上面接口讲解的一模一样,不用改。
1、C# 代码如下:
使用 Get 获取变量,使用 Set 修改变量,当然最后不用了记得释放:
// 获取Table
LuaTable table = LuaMgr.GetInstance().Global.Get<LuaTable>("myClass2");
Debug.Log(table.Get<int>("age"));
Debug.Log(table.Get<bool>("sex"));
Debug.Log(table.Get<string>("name"));
table.Get<LuaFunction>("myFunc").Call();
// 修改变量
table.Set("age", 100);
// 最后要释放
table.Dispose();
四、Lua 调用 C#
(一)前言
在第三节 C# 调用 Lua 的前言中,我已经说过如何通过 C# 调用 Main.lua,Main.lua 调用其他 lua 脚本来做到 C# 调用 Lua
在 Lua 调用 C# 中,我们主要逻辑都写在 lua 脚本中,但是运行时,我们并不是从 Lua 脚本作为入口去启动,因为 Lua 本身并不会识别 C# 中的数据,所以我们必须从 Unity 中启动,通过 C# 调用 Main.lua 的方式,这样 Xlua 会帮我们处理,让 Lua 可以识别并使用我们 C# 中的数据
(二)Lua 调用 C# 类
Lua 想要调用 C#,Xlua 已经帮我们处理好了,只通过 CS 这个特殊的表(根入口),就可以访问 C# 中的任何东西了,固定套路:CS.命名空间.类
1、实例化对象
Lua 中并没有 new 这个方法,但是 xlua 中,我们可以通过 () 的方式来实例化一个 C# 类,这里的 () 相当于类似无参构造,括号中传入参数就类似有参构造
Lua 代码如下:
local obj = CS.UnityEngine.GameObject()
GameObject = CS.UnityEngine.GameObject -- 可以取别名,方便书写
local obj2 = GameObject("Obj2")
运行,可以发现实例化了 2 个空物体,其中一个是带有 Obj2 名字的
2、调用静态方法
静态方法通过.来调用
Lua 代码如下:
local obj = GameObject.Find("Obj2")
3、调用成员方法
成员方法必须使用:调用,否则很容易出错,因为 C# 的成员方法一般都会用到自己的成员变量,因此 Lua 调用 C# 的成员方法时,必须使用冒号调用!这样默认就会把调用者(类对象)作为第一个参数传入
Lua 代码如下:
obj.transform:Translate(Vector3.right)
4、调用自定义类
如果我们的自定义类没有放在任何命名空间下,那么直接 CS.类名就好了,比如我在 C# 创建了一个 Test 类,那么实例化这个类就可以这样:
Lua 代码如下:
local test = CS.Test()
5、调用带泛型的方法
Lua 中并不支持泛型,因此,我们不能调用带泛型的方法,我们只能换种方案,通过 Type 的方式调用。xlua 为我们提供了 typeof 这个方法,获取变量的类型。比如我有一个 LuaCallCSharp 的脚本,想通过 AddComponent 添加到物体上
Lua 代码如下:
obj:AddComponent(typeof(CS.LuaCallCSharp))
(三)Lua 调用 C# 枚举
1、枚举的使用
固定套路就是:CS.命名空间.枚举.枚举名,比如我想使用 unity 的一个枚举和一个方法来创建立方体
Lua 代码如下:
PrimitiveType = CS.UnityEngine.PrimitiveType -- 枚举
GameObject = CS.UnityEngine.GameObject
local cube = GameObject.CreatePrimitive(PrimitiveType.Cube)
自定义枚举套路一样
2、枚举的转换
关键方法:__CastFrom(值),可以将对应值转换成枚举类型,下面举个小栗子:
C# 代码如下:
public enum MyEnum
{
Idle,
Move,
Jump
}
Lua 代码如下:
-- 数值转枚举
local a = CS.MyEnum.__CastFrom(1) -- Move
-- 字符串转枚举
local b = CS.MyEnum.__CastFrom("Jump") -- Jump
结果就是 a 值为 Move(C# 索引默认从 0 开始),b 值为 Jump
(四)Lua 调用 C# 数组、List、Dictionary
1、C# 代码如下:
public class Test
{
public int[] array = new int[5] { 1, 2, 3, 4, 5 };
public List<int> list = new List<int>();
public Dictionary<int, string> dict = new Dictionary<int, string>();
}
2、Lua 访问 C# 数组代码如下:
我们访问的是 C# 的数组,因此按照 C# 的方式访问就行,并且在遍历时无法使用#获取长度,因为 C# 数组在这里是 userdata 类型,而非 table 类型
local obj = CS.Test()
-- 访问数组
print(obj.array.Length)
print(obj.array[0]) -- 获取数组元素按C#的来就行
for i=0,obj.array.Length-1 do
print(obj.array[i])
end
3、Lua 创建 C# 数组代码如下:
因为我们无法直接 new 一个数组,因此可以调用 Array 类中的 CreateInstance 方法来创建一个数组
local array = CS.System.Array.CreateInstance(typeof(CS.System.Int32),10)
4、Lua 访问 List 代码如下:
obj.list:Add(0)
obj.list:Add(1)
obj.list:Add(2)
for i=0,obj.list.Count-1 do
print(obj.list[i])
end
5、Lua 创建 List 代码如下:
新版本的方式更加容易理解,新版本的第一句代表创建了一个 List
-- 老版本:`是esc下面那个键,后面的数字1代表有1个参数类型
local list = CS.System.Collections.Generic["List`1[System.String]"]()
-- 新版本:xlua版本大于v2.1.12
local list_String = CS.System.Collections.Generic.List(CS.System.String) -- 这样创建并不是实例化对象,而是一个类
local list = list_String()
6、Lua 访问 Dictionary 代码如下:
obj.dic:Add(1,"123")
for k,v in pairs(obj.dic) do
print(k,v)
end
7、Lua 创建 Dictionary 代码如下:
老版本和 List 差不太多,不过这里只列出新版本的,因为更推荐使用新版本方式创建:
local Dict_String_Vector3 = CS.System.Collections.Generic.Dictionary(CS.System.String,CS.UnityEngine.Vector3)
local dict = Dic_String_Vector3()
(五)Lua 调用 C# 拓展方法
我们先为 C# 的 Test 类写一个拓展方法,注意 Lua 中要使用拓展方法,需要在C#中加上 [LuaCallCSharp] 特性
1、C# 代码如下:
[LuaCallCSharp]
public static class Tools
{
public static void Move(this Test obj)
{
Debug.Log("Test移动");
}
}
2、Lua 代码如下:
local obj = CS.Test()
obj:Move()
(六)Lua 调用 C# ref 和 out 函数
1、C# 代码如下:
3 个测试函数
public class Test
{
public int RefFunc(int a, ref int b, int c)
{
b = 2;
return 1;
}
public int OutFunc(int a, out int b, int c)
{
b = 1;
return 2;
}
public int RefOutFunc(int a, out int b, ref int c)
{
b = 1;
c = 2;
return 3;
}
}
调用 ref 或者 out 函数时,第一个返回值是 return 的那个值,后面的就是 ref 或 out 的值,从左到右一一对应
2、Lua 调用 ref 函数:
这里 ref 少传参数的话,后面的参数会补为 nil,所以 ref 需要填占位的值
local obj = CS.Test()
local i,j = obj:RefFunc(1,0,1)
print(i,j) -- i为1,j为2
3、Lua 调用 out 函数:
out 可以不传占位置的参数,即少传参数的话,会先跳过 out 修饰的参数,这里我只传了 2 个参数,其中 1 传给了 a,2 传给了 c,b 跳过了
local i,j = obj:OutFunc(1,2)
print(i,j) -- i为2,j为1
4、Lua 调用 ref 和 out 函数:
这里同样,1 传给了 a,2 传给了 c,b 被跳过了
local i,j,k = obj:RefOutFunc(1,2)
print(i,j,k) -- i为3,j为1,k为2
简单总结下:ref 传参需要占位,out 传参可以不用占位
(七)Lua 调用 C# 重载函数
1、C# 代码如下:
4 个测试函数
public class Test
{
public int Func()
{
return 100;
}
public int Func(int a, int b)
{
return a + b;
}
public int Func(int a)
{
return a;
}
public float Func(float a)
{
return a;
}
}
2、Lua 代码如下:
我们先调用前两个函数,发现是可以调用的,说明 C# 的重载函数是可以被调用的,但是后两个会出现冲突,只有 int 的这个是正常的,float 的结果是 0,因为 lua 无法区分 int 和 float,他只有 number 类型
local obj = CS.Test()
print(obj:Func()) -- 100
print(obj:Func(10,5)) -- 15
print(obj:Func(1)) -- 1
print(obj:Func(1.2)) -- 0
解决方法:
通过 xlua 提供的反射机制,就可以做到区分所有重载函数啦,主要分 3 步:
- 反射获取 C# 函数
- 转成 lua 函数
- 直接调用
-- 1、获取c#函数信息
local m1 = typeof(CS.Test):GetMethod("Func",{typeof(CS.System.Int32)}) -- 得到Int重载函数信息
local m2 = typeof(CS.Test):GetMethod("Func",{typeof(CS.System.Single)}) -- 得到Float重载函数信息
-- 2、转成lua函数
local f1 = xlua.tofunction(m1)
local f2 = xlua.tofunction(m2)
-- 3、使用
print(f1(obj,10))
print(f2(obj,10.5))
(八)Lua 使用 C# 委托和事件
1、C# 代码如下:
声明了一个事件一个委托,但是事件由于只能在类内部调用,因此要提供一些方法给外部
public class Test
{
public UnityAction action;
public event UnityAction eventAction;
// 事件只能在内部调用,因此提供调用方法
public void DoEvent()
{
if (eventAction != null)
eventAction();
}
public void ClearEvent()
{
eventAction = null;
}
}
2、Lua 代码如下:
先准备要添加到委托或事件的函数:
local obj = CS.Test()
-- 要添加的函数
local func = function()
print("lua函数Func")
end
3、Lua 调用委托代码如下:
第一次添加委托要用 = 赋值,后面用 + 的方式
-- 添加
obj.action = func
obj.action = obj.action + func
-- 调用委托
obj.action()
-- 删除委托
obj.action = obj.action - func
-- 清除
obj.action = nil
4、Lua 调用事件代码如下:
事件的添加和删除比较不一样,需要通过括号中指明操作符和函数,类似使用成员方法
-- 添加
obj:eventAction("+",func)
obj:eventAction("+",func)
-- 调用事件
obj:DoEvent()
-- 删除
obj:eventAction("-",func)
-- 清空
obj:ClearEvent()
(九)Lua 使用 C# 二维数组
1、C# 代码如下:
public class Test
{
public int[,] array = new int[,] { { 1, 2, 3 }, { 4, 5, 6 } };
}
2、Lua 代码如下:
-- 获取长度
print("行:"..obj.array:GetLength(0))
print("列:"..obj.array:GetLength(1))
-- 获取元素,无法通过[0][0]或[0,0]方法获取元素,需要通过Array类方法
print(obj.array:GetValue(0,0))
print(obj.array:GetValue(1,1))
-- 遍历
for i=0,obj.array:GetLength(0)-1 do
for j=0,obj.array:GetLength(1)-1 do
print(obj.array:GetValue(i,j))
end
end
(十)Lua 使用 c# 的 null 和 nil 比较
1、引入
我们想创建一个空物体,并尝试获取身上的 Rigidbody 脚本,如果没有则添加该脚本,先来看一个错误示范:
Lua 代码如下:
GameObject = CS.UnityEngine.GameObject
Rigidbody = CS.UnityEngine.Rigidbody
local obj = GameObject("测试加脚本")
local rig = obj:GetComponent(typeof(Rigidbody))
-- lua中nil和null没办法进行==比较
if rig == nil then
rig = obj:AddComponent(typeof(Rigidbody))
end
此时是无法向 obj 身上添加 Rigidbody 脚本的,因为 rig 并不是 nil 类型,我们知道 rig 此时是 C# 中的 null,而 Lua 是没有 null 这个概念的,rig 的类型在 Lua 中是 usesrdata 类型,而非 nil。而 rig 为 nil 的情况要么是没初始化,要么是主动赋值了 nil。
幸运的是我们有多种方式来判断 C# 的 null 类型,下面就来介绍
2、方式一
使用 Equals 方法,xlua 为我们特殊处理了这个 Equals 方法,让 null 可以和 nil 进行比较。当然目前这样比较还是不够严谨,因为 rig 本身如果是 nil 时就会报错
if rig:Equals(nil) then
rig = obj:AddComponent(typeof(Rigidbody))
end
3、方式二
自己在 Lua 中写一个 IsNull 方法判断,后面调用该方法即可判断,我更推荐这种方式
Lua 代码如下:
function IsNull(object)
if object == nil or object:Equals(nil) then
return true
end
return false
end
if IsNull(rig) then
rig = obj:AddComponent(typeof(Rigidbody))
end
4、方式三
可以在 C# 中写个 IsNull 方法判断,比如我为 Object 方法写了一个拓展方法 IsNull(拓展方法记得加 [LuaCallCSharp] 特性哦!这里省略了)
public static bool IsNull(this UnityEngine.Object obj)
{
return obj == null;
}
之后再 Lua 中调用就行了,不过这个前提也是 rig 不为 nil
Lua 代码如下:
if rig:IsNull() then
rig = obj:AddComponent(typeof(Rigidbody))
end
(十一)Lua 使用 C# 协程
使用 xlua 的 util 表里的 cs_generator 函数,可以将 lua 函数转换成适配 c# 协程格式的函数,这样我们就可以在 Lua 中开启协程了:
1、准备工作
先在 Lua 中取好别名,获取 xlua 的 util 工具表,并创建一个 GameObject,上面挂载的LuaCallCSharp脚本其实就是继承MonoBehaviour的类,用于开启协程
Lua 代码如下:
GameObject = CS.UnityEngine.GameObject
WaitForSeconds = CS.UnityEngine.WaitForSeconds
-- xlua提供的工具表
util = require("xlua.util")
local obj = GameObject("Coroutine")
local mono = obj:AddComponent(typeof(CS.LuaCallCSharp))
2、开启协程
之后我们创建一个协程函数,并调用 StartCoroutine 开启协程:
-- 创建协程函数
func = function()
local a = 1
while true do
coroutine.yield(WaitForSeconds(1))
print(a)
a = a + 1
if a >= 5 then
mono:StopCoroutine(routine)
end
end
end
-- 开启协程
-- mono:StartCoroutine(func) 报错,无法将lua函数传入协程中
routine = mono:StartCoroutine(util.cs_generator(func)) -- 需要调用xlua的util中的cs_generator函数
(十二)Lua 使用 C# 泛型函数
1、前言
我们之前说过 Lua 是不能直接调用 C# 的泛型函数的,会有问题。经过测试,lua 直接调用泛型函数时,只支持约束为 class 且有形参的泛型函数,而且需要传入正确类型的参数,否则会变成 null 传入,下面简单举个例子:
C# 代码如下:
public class Test
{
public class Father {}
public class Child : Father {}
public void TestFunc<T>(T a,T b) where T : Father
{
Debug.Log("有参数有约束的泛型");
}
}
Lua 中,我们尝试直接调用这个泛型方法:
Lua 代码如下:
local obj = CS.Test()
local father = CS.Test.Father()
local child = CS.Test.Child()
obj:TestFunc(child,father)
当然,直接调用的话只有这种泛型是可以使用的,但是如果没有约束,或者没有形参,或者约束的不是 class,那么就无法直接调用了。
如果我们想完全能够调用 C# 的泛型函数,需要一些特殊的方法,下面就来讲解。
2、解决方法
首先是 C# 部分,我们再写一个泛型方法用于测试
C# 代码如下:
public void TestFunc2<T>(T a)
{
Debug.Log(a);
}
接下来就是 Lua 调用泛型方法了,主要分三步:
- 调用 xlua 表的
get_generic_method方法,传入类和泛型方法名,获取一个泛型模板函数 - 调用模板函数,传入一个泛型的类型,就可以获得指定泛型类型的函数
- 直接使用该泛型函数即可,但注意第一个参数是要执行泛型方法的对象,后面才是泛型方法的参数
Lua 代码如下:
local obj = CS.Test()
-- 获取泛型模板函数
local TestFunc2 = xlua.get_generic_method(CS.Test,"TestFunc2")
-- 获取明确类型的泛型函数
local TestFunc2_Int = TestFunc2(CS.System.Int32)
-- 调用
TestFunc2_Int(obj,2)
当然使用 C# 泛型方法并不是完美的,还是有限制条件在的,具体发生在打包方式上:
-
当我们使用 Mono 打包,没有问题,完全可以使用
-
但使用 IL2CPP 打包,只有泛型参数是引用类型,才完全可以使用
- 如果是值类型,只有 C# 代码显式调用过了对应值类型的泛型函数,才可以使用,比如 C# 中用过了
Func<int>,Lua 中使用 int 调用泛型函数 Func 时才可以找到对应实现
- 如果是值类型,只有 C# 代码显式调用过了对应值类型的泛型函数,才可以使用,比如 C# 中用过了
