前言
在进行lua方法注册的时候,大多数解决方案直接否定了泛型方法,因为在lua侧难以表达出泛型,以及lua的函数重载问题,
函数重载问题可以通过一些特殊方法解决,而泛型问题是主要问题,以Unity+Slua的情况来说
比如下面的类:
publicclassFoo { publicstaticvoidGetTypeName(System.Typetype) { Debug.Log(type.Name); } publicstaticvoidGetTypeName<T>() { Debug.Log(typeof(T).Name); } }
一般只会生成 GetTypeName(System.Typetype)
的注册方法.
那么泛型的方法在Lua那边该怎样注册才能让这个调用能够实现呢?一般来说我们调用泛型方法必须在写代码的时候就确定,像这样:
Foo.GetTypeName<int>(); //输出Int32
而lua并不能这样约束,它的调用必须还是非泛型的才可以,这是第一个问题,而第二个问题是lua那边怎样写?我们希望它的写法能跟C#保持
一致,或者相似吧,让人看起来容易明白,可是lua中中括号是大于小于号,不能这样写,想想有没有什么办法
因为在lua中是没有类型的,类型必须来自C#,所以只能将泛型作为非泛型方法才能使用,如果让函数进行一次退化和封装,像下面这样
--先将C#的typeof注册成全局函数,注册System.Int32命名为int localFoo={} Foo.GetTypeName=function(type) returnfunction() print(type.Name) end end
Foo.GetTypeName(typeof(int))(); --lua Foo.GetTypeName<typeof(int)>(); //C#
这样写的话,除了尖括号,基本就能两边一致了对吧,运行结果也是一样的
/*至于怎样注册typeof(int)*/ //在LuaState的Init中注册个全局函数[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] internalstaticintgetType(IntPtrL) { System.Typetype=null; LuaObject.checkType(L,1,outtype); LuaObject.pushObject(L,type); return1; } //在LuaState的Init中自己注册咯LuaDLL.lua_pushcfunction(L,getType);LuaDLL.lua_setglobal(L,"typeof");
//CustomExport.OnAddCustomClass中添加类型别名 add(typeof(System.Int32),"int");//int
只是这里lua的函数没有进行C#那边的调用啊,下一步就来看看有没有什么办法来实现调用.
如果通过自动注册的话,Foo应该是一个已经注册的类型.
[SLua.CustomLuaClass] publicclassFoo
并且有元表,元表里面有非泛型的GetTypeName方法了.现在先不要去动元表,
直接注册这个到Table里面,因为如果Table里面有值的话,就不会去查询元表了
import"Foo"; Foo.GetTypeName(typeof(int)); //输出Int32 rawset(Foo,"GetTypeName",function(type) returnfunction() localmt=getmetatable(Foo) localfunc=rawget(mt,"GetTypeName"); func(type) end end) Foo.GetTypeName(typeof(int))(); //输出Int32--注意返回了function然后再次调用
这个方法比较流氓,因为直接默认了有非泛型函数,并且覆盖了元表的非泛型方法,不可取的.
要继续的话,首先来看看一个泛型方法怎样通过Type方法进行调用的:
varmethods=typeof(Foo).GetMethods(BindingFlags.Public|BindingFlags.Static|BindingFlags.InvokeMethod); foreach(varmethodinmethods) { if(method.IsGenericMethod) { varparamters=method.GetParameters(); if(paramters==null||paramters.Length==0) { vargenericMethod=method.MakeGenericMethod(newType[]{typeof(int)}); if(genericMethod!=null) { genericMethod.Invoke(null,null); //输出Int32 break;} } } }
当然是反射啦,这样就能让泛型方法退化为非泛型了,虽然是一个缓慢的反射,不过时间基本只花费在Invoke上,问题还不大.
剩下的问题是重载了,有非泛型和泛型的两个同名函数,为了测试我先删除掉非泛型,
[SLua.CustomLuaClass] publicclassFoo { //publicstaticvoidGetTypeName(System.Typetype) //{ //Debug.Log(type.Name); //} publicstaticvoidGetTypeName<T>() { Debug.Log(typeof(T).Name); } }
生成的lua注册代码也要修改一下
System.Typea1; checkType(l,1,outa1); Foo.GetTypeName(a1);//它完了 pushValue(l,true);
改成
System.Typea1; checkType(l,1,outa1); varmethods=typeof(Foo).GetMethods(System.Reflection.BindingFlags.Public |System.Reflection.BindingFlags.Static |System.Reflection.BindingFlags.InvokeMethod); foreach(varmethodinmethods) { if(method.IsGenericMethod) { varparamters=method.GetParameters(); if(paramters==null||paramters.Length==0) { vargenericMethod=method.MakeGenericMethod(newType[]{typeof(int)}); if(genericMethod!=null) { genericMethod.Invoke(null,null); break; } } } } pushValue(l,true);
试试运行一下看看,输出Int32看来没有问题,问题是在Lua那边还是需要手动封装了一遍:
rawset(Foo,"GetTypeName",function(type) localmt=getmetatable(Foo) localfunc=rawget(mt,"GetTypeName"); func(type) end) --问题是,不进行一次rawset无法得到泛型写法 Foo.GetTypeName(typeof(int)); //输出Int32--Table方法
到这里,基本就可以得出结论了,
一.在lua中可以通过封装(闭包)的方式接近C#的泛型的写法,差别只是一个中括号和小括号
Foo.GetTypeName(typeof(int))(); --lua Foo.GetTypeName<typeof(int)>(); //C#
然而过程异常复杂,比如上述代码中的rawset过程需要在C#的注册代码中进行实现,而在调用的地方需要通过反射,并且在lua侧需要解决函数重载的问题,
上面的例子直接做了覆盖.就无法正常访问非泛型方法函数了.
二.既然泛型方法可以退化为非泛型,那么可以直接检测有没有同名的且同参数的非泛型函数,如果没有就把泛型方法的非泛型版添加到注册函数中即可.
上述就是C#学习教程:C#泛型方法在lua中表示的一种设计详解分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/908380.html