Xlua

C#调用Lua

lua解析器

lua解析器可以让我们在unity中执行Lua,一般情况下,我们会保证它在unity中的唯一性。

//创建一个lua解析器
LuaEnv env = new LuaEnv();
//执行Lua语言
env.DoString("print('你好世界')");
//像上面这样一句一句的执行lua脚本是非常不好的,所以可以使用lua脚本加载的知识来解决这个问题。
//xlua默认的脚本寻找路径是在Resources文件夹中,但是由于unity并不能识别Lua文件,所以直接加载时会报错的。解决方案是我们将lua后缀多加一个txt,将其转换为文本文件。
env.DoString("require('Main')");
//此方法可以帮助我们清除Lua中我们没有手动释放的对象,相当于垃圾回收。
//我们可以在帧更新中定时执行,也可以在切换场景时执行。
env.Tick();
//销毁Lua解析器,一般会用的会比较少,因为我们之前说过了,我们基本会保证Lua解析器在unity中是全局唯一的。
env.Dispose();

lua文件重定向

//需要调用此方法来添加新的路径,参数为委托类型
//delegate byte[] CustomLoader(ref string filepath)
env.AddLoader(NewLoader);
//这里我们写一个方法并传进去
private byte[] NewLoader(ref string filepath)
{//路径拼接需要加上后缀string path = Application.dataPath + "/Lua/" + filepath + ".lua";if (File.Exists(path)){return File.ReadAllBytes(path);}return null;
}
//由于委托可以注册多个方法,所以xlua的文件重定向查找方案也是优先查找委托中所有的路径,最后查找默认路径Resources。
//一旦找到对应文件就会返回。不再继续

获取全局变量

//首先我们需要编辑一个test.lua脚本并书写一下信息
print("Test脚本");
testNumber = 1;
testBool = true;
testFloat = 1.2;
testString = "你好";
//在之前的LuaManager中我们添加了一个Global属性,用于获得lua解析器的_G表。
//我们调用xlua的Global中的get方法来获取lua解析器读取的lua脚本中_G表所存放的全局变量。
//注意,字符串内容需要与lua中的变量名一致。
//这里是值拷贝,就算使用临时变量接受了返回值并进行改变也不会影响Lua脚本中的值。
print(LuaManager.GetInstance.Global.Get<int>("testNumber"));
print(LuaManager.GetInstance.Global.Get<bool>("testBool"));
//虽然lua只有一种数值类型number,但是我们可以根据其内容来在C#中为其赋予类型
print(LuaManager.GetInstance.Global.Get<float>("testFloat"));
print(LuaManager.GetInstance.Global.Get<string>("testString"));
//想要改变Lua脚本中的值,我们需要使用Global提供给我们的Set方法。
LuaManager.GetInstance.Global.Set("testNumber",5);
print(LuaManager.GetInstance.Global.Get<int>("testNumber"));

获得全局函数

//我们在test.lua中添加新成员
testFunc1 = function()print("这是一个无参无返回值的函数");
end
testFunc2 = function(param)print("这是一个有参有返回值的函数");return param + 1;
end
testFunc3 = function()print("这是一个多返回值的函数");return 1,2,"123";
end
testFunc4 = function(...)arg = {...};print("这是一个有变长参数的函数");for k,v in pairs(arg) doprint(k,v)end
end
//总的来说,获得lua的全局函数需要使用到委托。
//这里我们可以使用自定义委托,C#提供的标准委托,或是使用Xlua提供的委托。
//注意使用Xlua提供的委托可能发生拆箱装箱
//我们先来看第一种情况,无参无返回值,我们直接使用Action委托来接受。
Action func1 = LuaManager.GetInstance.Global.Get<Action>("testFunc1");
func1.Invoke();
//第二种情况,有参有返回值,我们直接使用Func委托来接受。Func<int, int> func2 = LuaManager.GetInstance.Global.Get<Func<int, int>>("testFunc2");
func2.Invoke(10);
//第三种情况,多返回值的函数,我们需要自定义委托来接受。首先我们自定义委托
//当我们要使用自定义委托来接受lua中的全局函数时,我们需要为委托添加[CSharpCallLua]特性,并重新加载。
[CSharpCallLua]
public delegate void FuncHandler(out int paramA,out int paramB, out string paramC);
//使用此委托接受
FuncHandler func3 = LuaManager.GetInstance.Global.Get<FuncHandler>("testFunc3");
int paramA;
int paramB;
string paramC;
func3.Invoke(out paramA,out paramB,out paramC);
//第四种情况,要求输入一个变长参数,我们依旧需要自定义委托来接受。
[CSharpCallLua]
public delegate void FuncHandler2(params object[] array);
FuncHandler2 func4 = LuaManager.GetInstance.Global.Get<FuncHandler2>("testFunc4");
func4.Invoke(1,2,3,"qwe",true);

List,Dictionary映射Table

//使用List和Dictionary映射Table和之前说过的获得全局变量是一样的。
//来到lua脚本我们添加一些表
testList1  = {1,2,3,4,5,6};
testList2 = {1,2,3,"123",true,false};testDic1 = {["1"] = 1,["2"] = 2,["3"] = 3,["4"] = 4,
}
testDic2 = {["1"] = 1,["2"] = 2,["3"] = true,["4"] = false,
}
//来到C#使用list或者dictionary直接接受
//注意这里的接受依旧是值拷贝
List<int> testList1 = LuaManager.GetInstance.Global.Get<List<int>>("testList1");
foreach (var item in testList1)
{print(item);
}List<object> testList2 = LuaManager.GetInstance.Global.Get<List<object>>("testList2");
foreach (var item in testList2)
{print(item);
}
Dictionary<string, int> testDic1 = LuaManager.GetInstance.Global.Get<Dictionary<string, int>>("testDic1");
foreach (var key in testDic1.Keys)
{print($"{key}_{testDic1[key]}");
}
Dictionary<string, object> testDic2 = LuaManager.GetInstance.Global.Get<Dictionary<string, object>>("testDic2");
foreach (var key in testDic2.Keys)
{print($"{key}_{testDic2[key]}");
}

类映射Table

//使用C#类映射lua中的类表
//首先我们先在lua中写一个类表,其中包含number类型,string类型,boolean类型和function类型
testClass = {myInt = 1,myString = "你好",myBool = true,myFunc = function()print("testClass的函数");end
}
//接下来我们来书写C#中准备接受这个表的类
//其中,类名实没有要求的,但是在这个类中声明的成员变量名称需要和lua中的名称一致,这也是首次要求名称对应。
//类的成员变量需要是公共的,或者internal,其他类型将无法赋值。
//我们不需要将lua中声明的类型在这里全部声明;不论是多出来的变量还是缺少的变量,Xlua都会选择忽略。
public class CallLuaClass{public int myInt;public string myString;public bool myBool;public Action myFunc;}
//使用类映射table依旧是值拷贝

接口映射Table

接口映射Table和类大同小异,但在使用时需要注意:

  1. 接口需要统一使用属性来接受lua脚本中的成员。
  2. 使用接口映射Table时,需要和自定义委托一样加上特性并重新编译。
  3. 如果改变了接口的内容,一定要先清除在编译。
  4. 接口是引用拷贝,改变接口的值,lua中的值也会改变。

LuaTable,LuaFunction

首先要说的是,官方不推荐使用luaTable接收lua中的Table,也不推荐使用LuaFunction接收Lua中的function,他们执行效率低,且需要手动释放资源。

但需要注意的是。LuaTable和LuaFunction是引用拷贝,可以通过set方法改变lua脚本中的值。

*Lua调用C#

Lua使用C#类

print("*********Lua调用C#类相关知识点*********");
--对于lua调用C#的方法,我们一定是通过C#先进入Lua的
--通过DoString方法进入--lua中使用C#的类非常简单
--固定套路
--CS.命名空间.类名
--Unity的类 比如 GameObject , Transform等等 可以通过CS.UnityEngine.类名进行调用
--CS.UnityEngine.GameObject
--CS.UnityEngine.Transformprint("通过C#中的类在Lua中实例化一个游戏对象");--由于Lua中没有new,所以在Lua中我们有另外的语法来创建对象。
--在Lua中类名括号就是在实例化对象,默认的调用的相当于就是无参构造函数。
--调用无参构造函数
local gameObject1 = CS.UnityEngine.GameObject();
--调用有参构造函数
local gameObject2 = CS.UnityEngine.GameObject("有参构造函数对象");--为了方便使用并节约性能,定义全局变量存储C#中的类
--相当于取了一个别名
GameObject = CS.UnityEngine.GameObject;
local gameObject3 = GameObject("这是一个取了别名的游戏对象");--类中的静态对象可以直接使用.运算符来调用
local gameObject4 = GameObject.Find("这是一个取了别名的游戏对象");--得到对象中的成员变量,直接使用.运算符即可
print(gameObject4.transform.position);
Debug = CS.UnityEngine.Debug;
Debug.Log(gameObject4.transform.position);
--如果使用对象中的成员方法一定要使用:
Vector3 = CS.UnityEngine.Vector3;
gameObject4.transform:Translate(Vector3.right);
Debug.Log(gameObject4.transform.position);--使用不继承Mono的普通类,首先在C#中创建一个Test类
Test = CS.Test;
--创建一个对象
local test = Test();
test:Speak("123");--继承Mono的类。
--注意继承Mono的类是不可以直接new的,我们需要使用AddComponent方法
local gameObject5 = GameObject("加脚本测试");
--通过GameObject的AddComponent添加脚本
--Xlua提供了一个重要的方法typeof可以获得类的Type
--Xlua中不支持无参泛型函数,所以我们需要使用AddComponent的另一个重载
gameObject5:AddComponent(typeof(CS.Test1_LuaEnv));

Lua使用C#枚举

print("*********Lua调用C#枚举相关知识点*********");--Lua调用C#的枚举,和调用C#的类相似,直接使用.运算符来使用其枚举值
--调用Unity中的默认枚举
--首先参照lua调用类,我们先将枚举的命名空间保存
PrimitiveType = CS.UnityEngine.PrimitiveType;
GameObject = CS.UnityEngine.GameObject;
--unity中创建默认几何体的代码
local obj = GameObject.CreatePrimitive(PrimitiveType.Cube);--自定义枚举,使用方法和unity的默认枚举一样,但需要注意命名空间
--我们联合枚举转换一起来说
E_MyEnum = CS.E_MyEnum;
print(E_MyEnum.idle);
--数值转枚举
local enum1 = E_MyEnum.__CastFrom(1);
print(enum1);
--字符串转枚举
local enum2 = E_MyEnum.__CastFrom("atk");
print(enum2);

Lua使用C#数组,List,Dictionary

print("*********Lua调用C#数组相关知识点*********");
--需要注意,虽然我们在Lua中使用表来模拟数组,list,dictionary,但是当lua调用C#时,我们要遵守C#的使用规则。
--首先我们现在Lua中创建一个C#的数组
--这里我们调用C#中Array基类的CreateInstance方法来创建,具体参数信息需要查阅C#
--这里我们使用的方法第一个参数传入type,第二个参数传入容量
local array = CS.System.Array.CreateInstance(typeof(CS.System.Int32),10);
--获得数组的长度,这里调用了C#中的成员变量
print(array.Length);
--访问数组的元素
print(array[0]);
--数组遍历,这里需要注意,虽然Lua的索引是从1开始,但是我们使用的是C#的数组,其索引是从0开始的。
--注意Lua中的for循环遍历一定要减1
for i=1,array.Length - 1 doprint(array[i]);
endprint("*********Lua调用C#List相关知识点*********");
--首先我们还是先创建一个List对象,这里有两种方式。
--老版本
--其中CS.System.Collections.Generic是List泛型的命名空间,List`1指的是此泛型有一个泛型参数。
local list = CS.System.Collections.Generic["List`1[System.String]"]();
list:Add("5555");
print(list[0]);
--新版本
local List_String =  CS.System.Collections.Generic.List(CS.System.String);
local list2 = List_String();
list2:Add("123");
print(list2[0]);
--输出长度
print(list2.Count);
print("*********Lua调用C#Dictionary相关知识点*********");
--我们还是先来创建一个字典
local dictionary_Int_Vector3 = CS.System.Collections.Generic.Dictionary(CS.System.String, CS.UnityEngine.Vector3);
local dic = dictionary_Int_Vector3();
dic:Add("123",CS.UnityEngine.Vector3.right);
--除了int类型的键,其他无法通过索引直接获得,这里会直接报空
print(dic["123"]);
--我们通过以下方法来获得键值
--1. TryGetValue
print(dic:TryGetValue("123"));
--2. get_Item, set_Item
print(dic:get_Item("123"));
dic:set_Item("123",nil);
print(dic:get_Item("123"));

Lua使用C#拓展方法

print("*********Lua调用C#List相关知识点*********");
local ExMethodDemo = CS.ExMethodDemo();
--Lua调用C#的拓展方法时,需要在拓展方法的类上面加上[LuacallCSharp]这个特性,并重新加载
--使用拓展方法和使用成员方法一样使用:,毕竟拓展方法本身就是将自己作为参数传进去了。
ExMethodDemo:Eat();
--[[总结:1. 想要在Lua中使用拓展方法,就一定要加上[LuacallCSharp]特性。2. 建议Lua中要使用的C#类都使用这个特性,可以提升性能。3. Xlua是通过反射来调用C#类的,当我们加了上述特性并重新加载代码后,相当于提前做了工作。
]]

Lua使用ref和out

print("*********Lua调用C#ref和out相关知识点*********");
local RefAndOut = CS.RefAndOut();
--C#中的ref参数会以多返回值的形式返回给Lua。
--如果函数存在返回值,则第一个默认返回该返回值。
--之后返回的结果就是ref的结果,从左到右一一对应。
--ref参数需要传入一个默认值来占位,这点和C#中使用是非常相似的。
local paramA,paramB,paramC = RefAndOut:RefTest(1,0,0,1);
print(paramA);
print(paramB);
print(paramC);
--C#中的out参数会以多返回值的形式返回给Lua。
--如果函数存在返回值,则第一个默认返回该返回值。
--之后返回的结果就是out的结果,从左到右一一对应。
--与ref不同,out参数不需要占位,当然如果你传入参数也没有事。
local paramA,paramB,paramC = RefAndOut:OutTest(2,1);
print(paramA);
print(paramB);
print(paramC);
--ref和out的综合使用,总体区别在于ref需要占位,out不需要
local paramA,paramB,paramC = RefAndOut:RefOut(2,1,1,1);
print(paramA);
print(paramB);
print(paramC);

Lua使用C#重载方法

print("*********Lua调用C#重载方法相关知识点*********");--虽然lua自己不支持重载函数的书写,但是可以调用C#中的重载函数
--原因是lua中对于相同变量的函数赋值会覆盖之前赋值的函数,所以不能重载函数是,但如果调用C#的话很明显就不会存在这个问题。
local CallOverrideFunc = CS.LuaCallOverride();
CallOverrideFunc:CallOverrideWithParam();
--注意,由于lua中在数值类型上只支持number类型,所以对于C#中参数个数相同,数值精度不同的重载无法正确获得。
--我们要尽量避免这种写法,或者使用Xlua提供给我们的反射解决方案。
--需要注意的是,此方案毕竟是通过反射实现的,性能较差,不到万不得已的情况下不要使用。
print(CallOverrideFunc:CallOverrideWithParam(10));
print(CallOverrideFunc:CallOverrideWithParam(10.2));
--反射解决方案
--首先通过反射获得需要使用的方法
local method1 = typeof(CS.LuaCallOverride):GetMethod("CallOverrideWithParam", {typeof(CS.System.Int32)});
local method2 = typeof(CS.LuaCallOverride):GetMethod("CallOverrideWithParam", {typeof(CS.System.Single)});
--通过Xlua提供的tofunction方法转换为lua函数
--一般我们只转换一次,之后重复使用
local luafunc1 = xlua.tofunction(method1);
local luafunc2 = xlua.tofunction(method2);
--转换完成后,需要注意:
--如果目标方法为成员方法,则第一个参数需要传调用对象。
--如果目标方法为静态方法,则不需要。
print(luafunc1(CallOverrideFunc,10));
print(luafunc2(CallOverrideFunc,10.2));

Lua使用C#委托事件

print("*********Lua调用C#委托事件相关知识点*********");
--Lua使用C#委托事件的语法基本上和C#中的使用是一致的。
local LuaCallDelegate = CS.LuaCallDelegate();
local func1 = function()print("Lua使用c#委托调用lua的方法");
end
--Lua中使用C#委托,将其当作成员变量来使用,使用的规则和C#中一样,先等号赋值,再+=
--需要注意,lua中没有+=运算,需要我们老老实实的写A = A + B这种形式。
LuaCallDelegate.DelegateTest = func1;
--lua中使用C#委托,也可以直接添加lua函数,这种添加方法有点类似于在C#中添加匿名方法或者lambda表达式。
--不建议使用,因为这种添加形式只能加不能减。
LuaCallDelegate.DelegateTest = LuaCallDelegate.DelegateTest + function()print("lua使用C#委托添加的临时变量");
end
LuaCallDelegate.DelegateTest();
--注销回调函数
LuaCallDelegate.DelegateTest = LuaCallDelegate.DelegateTest - func1;
LuaCallDelegate.DelegateTest();
--清空委托链,相当于C#中调用 = null
LuaCallDelegate.DelegateTest = nil;local func2 = function()print("事件添加的函数");
end
--在事件的添加上,lua不同于C#
--事件添加函数使用对象名:事件名("+",函数名)的方式来添加
--注销事件同理
LuaCallDelegate:EventTest("+",func2);
LuaCallDelegate:OnEventStart();
--同样可以使用此方法来添加匿名函数
LuaCallDelegate:EventTest("+",function()print("这是使用事件添加的匿名函数");
end
);
LuaCallDelegate:OnEventStart();
--注销事件
LuaCallDelegate:EventTest("-",func2);
LuaCallDelegate:OnEventStart();
--由于事件不能在类外调用委托链清空,所以我们将委托链清空的方法封装在事件申明类中。
LuaCallDelegate:ClearEvent();
LuaCallDelegate:OnEventStart();

Lua使用C#获得二维数组的值

print("*********Lua调用C#二维数组相关知识点*********");
--Lua使用C#的数组,并直接通过索引查找数组元素只适用于一维数组。
--想要获得多维数组的元素需要使用Array基类中的一个获取元素的方法GetValue。
--同理如果我们想要设置多维数组的值也可以通过方法SetValue来实现。
--注意这里的GetSet都为成员方法。
local Call2Array = CS.Call2Array();
--获得C#2维数组的行长度
print(Call2Array.My2Array:GetLength(0));
--获得C#2维数组的列长度
print(Call2Array.My2Array:GetLength(1));
--获得C#2维数组的元素,我们需要通过GetValue方法来实现。
--获得索引为(0,0)的元素print(Call2Array.My2Array:GetValue(0,0));--获得索引为(1,2)的元素print(Call2Array.My2Array:GetValue(1,2));--遍历C#二维数组
for i=0,Call2Array.My2Array:GetLength(0) - 1 dofor j=0, Call2Array.My2Array:GetLength(1) - 1 doprint(Call2Array.My2Array:GetValue(i,j));end
end

Lua调用C#nil和null的比较

print("*********Lua调用C#nil和null相关知识点*********");
--首先我们需要明白:C#中的null和lua中的nil是不能判等的。
--我们用一个开发中比较常见的问题来说明这个问题。
--问题:判断一个游戏物体是否存在rigidbody,如果不存在需要添加一个tigidbody组件。
local GameObject = CS.UnityEngine.GameObject;
local Rigidbody = CS.UnityEngine.Rigidbody;
local go = GameObject("添加Rigidbody");
local rigi = go:GetComponent(typeof(Rigidbody));
--解决方式一:使用Equals
if rigi:Equals(nil) thengo:AddComponent(typeof(Rigidbody));
end
--再次判断go物体上是否有Rigidbody组件
local rigi = go:GetComponent(typeof(Rigidbody));
--解决方式二:在Main中或者调用Main的调用函数中书写全局判空函数并使用。
--[[
function IsNull(param)if param == nil or param:Equals(nil) thenreturn true;endreturn false;
end
]]
if IsNull(rigi) thenprint("123");go:AddComponent(typeof(Rigidbody));
end
--再次判断go物体上是否有Rigidbody组件
local rigi = go:GetComponent(typeof(Rigidbody));
--解决方式三:在C#中为Object书写判空拓展方法并使用。
--[[
[LuaCallCSharp]
public static class NilAndNull{public static bool IsNull(this Object obj){return obj == null;}
}
]]
if rigi:IsNull() thenprint("123");go:AddComponent(typeof(Rigidbody));
end

CSharpCallLua和LuaCallCsharp特性

使用时机:

CSharpCallLua:当我们使用C#的接口和委托接收lua代码时(CSharp为主场),我们需要为C#类提供这个特性。另外一种情况是当我们需要使用Lua调用C#的系统类型的时候,同样需要使用到该特性。

LuaCallCsharp:当我们使用Lua调用C#时(Lua为主场),我们建议所有被lua调用的类都加上这个特性。

这两个特性保证了自定义C#类和lua代码之间的调用,但我们不能通过这两个特性实现系统类型和lua代码之间的调用。

Lua使用C#系统类型

上面对于两个特性的比较已经说明了其使用时机,但是我们注意到,当我们想用lua调用Csharp中系统类型时。我们没有办法为系统类型添加特性,这时我们就需要用到xlua的特性列表。

print("*********Lua调用C#系统类型相关知识点*********");
--这里我们用unity中的Slider为例
local GameObject = CS.UnityEngine.GameObject;
local Slider = CS.UnityEngine.UI.Slider;
local go = GameObject.Find("Slider");
print(go);
local sliderScript = go:GetComponent(typeof(Slider));
print(sliderScript);
--[[
这里直接添加监听事件,会直接报错提示需要为UnityAction添加[CSharpCallLua]的特性。
但我们知道我们是不可以改变系统类型的,所以我们需要用到xlua提供的特性列表:[CSharpCallLua]public static List<Type> CSharpCallLuaList = new List<Type>(){typeof(UnityAction<float>),};[LuaCallCSharp]public static List<Type> LuaCallCSharpList = new List<Type>(){typeof(GameObject),typeof(Rigidbody),};这两个分别为[CSharpCallLua]特性列表和[LuaCallCSharp]特性列表。我们只需要在里面写我们想要添加特性的类型就可以了,之后重新生成代码,Xlua就会帮我们构建可以使用的中间代码。特性列表还有一大用途就是集中添加我们需要申明特性的类,这会使我们的代码更加专业和简洁。完成特性列表的书写后我们就可以通过lua来调用C#或者unity中的系统类型了。需要注意:特性列表和特性列表所在的类一定都为静态的。
]]
sliderScript.onValueChanged:AddListener(function(value)print(value);
end);

Lua使用C#协程

print("*********Lua调用C#协程相关知识点*********");
local GameObject = CS.UnityEngine.GameObject;
local WaitForSeconds = CS.UnityEngine.WaitForSeconds;
--Lua调用C#的协程需要注意几点:
--[[1. 建议将所有用到的和C#协程有关的类都预先保存2. Lua不能直接使用C#协程中的yield return3. 虽然我们使用lua调用C#的StartCoroutine来开启协程,但是协程函数的书写使用Lua语法书写4. lua协程不能直接传入StartCoroutine中,我们需要接用xlua为我们提供的util工具表来实现5. 由于调用的是C#的协程启动和停止相关的函数,所以除过协程本身的书写,其他也还是遵循C#的规则
]]
--加载xlua提供给我们的util工具表
util = require("xlua.util");
--首先我们先来创建一个游戏物体
local go = GameObject("Coroutine");
local mono = go:AddComponent(typeof(CS.CsharpCallLua.CallCoroutine));--书写lua协程
func = function()local param = 1;while true do--使用lua的yield来暂停协程coroutine.yield(WaitForSeconds(1));print(param);param = param + 1;--如果param大于10,我们就停止协程if param > 10 thenmono:StopCoroutine(cor);endend
end--使用C#的StartCoroutine开启lua协程,这里使用util工具表辅助实现
cor = mono:StartCoroutine(util.cs_generator(func));

Lua使用C#泛型函数

print("*********Lua调用C#泛型相关知识点*********");
local callT = CS.CallT();
local chlid = CS.CallT.InnerChild();
local father = CS.CallT.InnerFather();--lua仅支持有约束有参数且约束为类的泛型函数
callT:TestFunc1(child,father);
callT:TestFunc1(father,child);--lua不支持没有参数的泛型函数
--callT:TestFunc2();
--lua不支持没有约束的泛型函数
--callT:TestFunc3(child);
--lua不支持非class的约束
--callT:TestFunc4();--补充知识:xlua为我们提供了对应的转换方法,但是慎用
--[[此方法的有一定的限制:1. 如果我们的游戏项目是Mono打包则这种方式是完全支持的;但如果我们的项目是IL2CPP打包,则只有引用类型可以使用。
]]
--[[1. 使用get_generic_method方法得到通用函数xlua.get_generic_method(类, 函数名字符串);2. 使用通用函数设置泛型类型3. 调用设置了泛型类型的通用函数。]]
local testFunc3 = xlua.get_generic_method(CS.CallT, "TestFunc3");
local testFunc3_R = testFunc3(CS.CallT.InnerFather);
--[[调用:1. 成员方法:第一个参数需要穿阿如调用函数的对象。2. 静态方法:不需要传调用对象。
]]
testFunc3_R(callT, father);

Xlua和C#使用简介相关推荐

  1. 基于xlua和mvvm的unity框架

    1.框架简介 这两天在Github上发现了xlua的作者车雄生前辈开源的一个框架-XUUI,于是下载下来学习了一下.XUUI基于xlua,又借鉴了mvvm的设计概念.xlua是目前很火的unity热更 ...

  2. lua 函数 默认值_Unity热更新框架之xLua

    一:xLua概述 二:Lua文件加载 三:xLua文件配置 四:Lua与C#交互 五:xLua热更新 一:xLua概述 1.1 xLua简介 xLua是由腾讯维护的一个开源项目,xLua为Unity. ...

  3. PowerDesigner简介(Yanlz+CDM+PDM+OOM+BPM+DMM+EAM+FEM+LDM+RQM+XSM+立钻哥哥++++)

    PowerDesigner简介 版本 作者 参与者 完成日期 备注 PowerDesigner_Intro_V01_1.0 严立钻 2019.01.15 ##<PowerDesigner简介&g ...

  4. OpenXR简介(Yanlz+Unity+VR+AR+MR+XR+VRTK+SteamVR+立钻哥哥+==)

    OpenXR简介 版本 作者 参与者 完成日期 备注 OpenXR_Intro_V01_1.0 严立钻 2019.01.03 ##<OpenXR简介>发布说明: ++++"Ope ...

  5. etcd 笔记(01)— etcd 简介、特点、应用场景、常用术语、分布式 CAP 理论、分布式原理

    1. etcd 简介 etcd 官网定义: A highly-available key value store for shared configuration and service discov ...

  6. Docker学习(一)-----Docker简介与安装

    一.Docker介绍 1.1什么是docker Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源 Docker可以让开发者打包他们的应用以及依赖包到一个轻量级,可移植 ...

  7. 【Spring】框架简介

    [Spring]框架简介 Spring是什么 Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Asp ...

  8. TensorRT简介

    TensorRT 介绍 引用:https://arleyzhang.github.io/articles/7f4b25ce/ 1 简介 TensorRT是一个高性能的深度学习推理(Inference) ...

  9. 谷粒商城学习笔记——第一期:项目简介

    一.项目简介 1. 项目背景 市面上有5种常见的电商模式 B2B.B2C.C2B.C2C.O2O B2B 模式(Business to Business),是指商家和商家建立的商业关系.如阿里巴巴 B ...

最新文章

  1. Android利用AccessibilityService自动获取微信号
  2. 删除安装的mysql数据库文件_安装/删除MySQL数据库
  3. Silverlight学习笔记(九)-----RenderTransform特效【五种基本变换】及【矩阵变换MatrixTransform】...
  4. Lecture 20 Parallel Algorithms I
  5. 天线发射功率计算公式_天线基本知识及应用―链路及空间无线传播损耗计算
  6. java poi读取word中附件_java poi word读取
  7. LeetCode《编程能力入门》刷题笔记(34 题全)
  8. getline函数(精华版)
  9. css鼠标拖拉卡顿_66个值得收藏的CSS开发技巧
  10. Linux安装java(jdk8)
  11. H5使用OCR身份证识别
  12. 服务器ssd内存性能对比,真是大快人心 九款240/256G SSD大横评
  13. 偷梁换柱:谨防“Synaptics”蠕虫病毒
  14. 全站即时通讯技术资料分类
  15. 操作系统原理课程 期末考试复习重点
  16. 统计-R(相关系数)与R^2(决定系数)傻傻分不清
  17. 企业云计算运营模式,主要分为哪3种运营模式?
  18. web应用开发相关技术,CSS盒模型概述
  19. 爬虫之爬取英雄联盟战绩详细数据
  20. sunday算法简介

热门文章

  1. UI设计文字排版四大原则
  2. Emgucv不完整图像分割试验(十九)——Emgucv实现ACE算法
  3. matlab设计滤波器的工具箱,matlab工具箱设计滤波器
  4. JavaWeb--JDBC核心技术
  5. Winform最基础的DBHelper类
  6. 【正一专栏】战长沙——血性尊严
  7. 01背包问题-动态规划算法(最简洁)
  8. vue key值的重复键问题报错
  9. 在Xcode7中搭建python开发环境
  10. SonarQube 9.x集成阿里p3c代码规范检测java代码;