xlua源码分析(六) C#与lua的交互总结
- Push
-
- 基础类型
- lua数据结构
- 值类型
- 引用类型
- Get
-
- 基础类型
- lua数据结构
- 值类型
- 引用类型
上一节我们分析了xlua对struct类型所做的优化,本节我们系统性地梳理一下xlua中C#与lua的交互。所谓C#与lua的交互,其实主要就分为两部分,第一是往lua层中传数据,第二则是从lua层中取数据。
Push
往lua层中传数据定义为Push,在C#的ObjectTranslator类中,可以看到Push所有支持类型到lua层的入口函数:
public void PushByType(RealStatePtr L, T v)
{
Action push_func;
if (tryGetPushFuncByType(typeof(T), out push_func))
{
push_func(L, v);
}
else
{
PushAny(L, v);
}
}
基础类型
int,double,string,bool等基础类型,在lua层中也能找到与之所对应的类型,所以处理起来最简单,直接调用lua API即可:
type | API |
---|---|
int | lua_pushinteger |
double | lua_pushnumber |
string | lua_pushstring |
bool | lua_pushboolean |
lua数据结构
之前的文章中分析过,xlua在C#中实现了映射lua table和lua function的类,默认无需额外生成代码的通用类叫做LuaTable和LuaFunction,以及通过接口或者委托的方式,生成代码的专用类XXXBridge和DelegateBridge类。它们都继承自LuaBase
基类,而且本质上表示的是lua对象,所以push到lua层,也应该是push所持有的lua对象,而不是它自身。持有的lua对象保存在lua的registry表中,C#类记录了其reference:
internal virtual void push(RealStatePtr L)
{
LuaAPI.lua_getref(L, luaReference);
}
值类型
为了性能考虑,C#层传递值类型时,对自定义的值类型,都会生成一个custom push函数,例如:
translator.RegisterPushAndGetAndUpdate(translator.PushUnityEngineVector2, translator.Get, translator.UpdateUnityEngineVector2);
translator.RegisterPushAndGetAndUpdate(translator.PushUnityEngineVector3, translator.Get, translator.UpdateUnityEngineVector3);
translator.RegisterPushAndGetAndUpdate(translator.PushUnityEngineVector4, translator.Get, translator.UpdateUnityEngineVector4);
translator.RegisterPushAndGetAndUpdate(translator.PushUnityEngineColor, translator.Get, translator.UpdateUnityEngineColor);
translator.RegisterPushAndGetAndUpdate(translator.PushUnityEngineQuaternion, translator.Get, translator.UpdateUnityEngineQuaternion);
translator.RegisterPushAndGetAndUpdate(translator.PushUnityEngineRay, translator.Get, translator.UpdateUnityEngineRay);
translator.RegisterPushAndGetAndUpdate(translator.PushUnityEngineBounds, translator.Get, translator.UpdateUnityEngineBounds);
translator.RegisterPushAndGetAndUpdate(translator.PushUnityEngineRay2D, translator.Get, translator.UpdateUnityEngineRay2D);
值类型主要分为两种,一是enum,一是struct。对于enum,由于enum是全局唯一的,所以xlua在lua层以一个全局的table记录它,enum中的每一个元素都对应table中的一个userdata。而struct则有两种映射方式,一种是userdata,userdata直接保存struct的数据,一种是table,struct中的每个字段对应了table的每个key。如果该值类型找不到相应的custom push函数,那就当做C#引用类型来处理了。
引用类型
C#引用类型的对象,会当作一个userdata,push到lua层,push之前会在C#层中进行缓存,并返回一个缓存的index,这个index将作为lua层缓存中的key,与userdata相关联。
Get
从lua层中取数据,则要复杂一些,因为C#类型相比于lua类型来说,要多得多。同样ObjectTranslator类中也有一个包含一切的Get函数:
public void服务器托管网 Get(RealStatePtr L, int index, out T v)
{
Func get_func;
if (tryGetGetFuncByType(typeof(T), out get_func))
{
v = get_func(L, index);
}
else
{
v = (T)GetObject(L, index, typeof(T));
}
}
基础类型
int,double,string,bool等基础类型,这些类型与push类似,直接调用API:
type | API |
---|---|
int | lua_tointeger |
double | lua_tonumber |
string | lua_tostring |
bool | lua_toboolean |
lua数据结构
与push时对应,要把lua栈上的值转换为LuaTable和LuaFunction这类对象,要么它是userdata,要么它就是对应的lua数据结构,此时get会把table或function缓存到registry表中,返回reference到C#层,用来创建LuaTable和LuaFunction对象。
private object getLuaTable(RealStatePtr L, int idx, object target)
{
if (LuaAPI.lua_type(L, idx) == LuaTypes.LUA_TUSERDATA)
{
object obj = translator.SafeGetCSObj(L, idx);
return (obj != null && obj is LuaTable) ? obj : null;
}
if (!LuaAPI.lua_istable(L, idx))
{
return null;
}
LuaAPI.lua_pushvalue(L, idx);
return new LuaTable(LuaAPI.luaL_ref(L), translator.luaEnv);
}
值类型
对于自定义的值类型,C#都有与之对应的custom get函数,如果lua栈上的值是userdata,就尝试从userdata中获取值类型中的数据;如果是table,就尝试根据值类型的字段名称,获取table中的值。这两种方法我们之前也都分析过了。
public void Get(RealStatePtr L, int index, out UnityEngine.Vector2 val)
{
LuaTypes type = LuaAPI.lua_type(L, index);
if (type == LuaTypes.LUA_TUSERDATA )
{
if (LuaAPI.xlua_gettypeid(L, index) != UnityEngineVector2_TypeID)
{
throw new Exception("invalid userdata for UnityEngine.Vector2");
}
IntPtr buff = LuaAPI.lua_touserdata(L, index);if (!CopyByValue.UnPack(buff, 0, out val))
{
throw new Exception("unpack fail for UnityEngine.Vector2");
}
}
else if (type ==LuaTypes.LUA_TTABLE)
{
CopyByValue.UnPack(this, L, index, out val);
}
else
{
val = (UnityEngine.Vector2)objectCasters.GetCaster(typeof(UnityEngine.Vector2))(L, index, null);
}
}
引用类型
对于一般的引用类型对象,C#层有一个genCaster函数,根据对象类型动态生成get函数。这个函数体比较庞大复杂,首先会判断lua栈上的值是否为userdata,如果是则尝试从C#对象缓存中根据userdata的key取出对应的object,取出成功则直接返回。但如果取出失败,则需要根据类型的不同,去处理不同的逻辑。
例如,如果引用类型为Delegate,则需要判断是否是lua function映射到delegate的情况,如果是则需要返回包装好lua function的delegate。这些包装函数也是自动生成的,位于DelegatesGensBridge这个文件中:
public override Delegate GetDelegateByType(Type type)
{
if (type == typeof(System.Action))
{
return new System.Action(__Gen_Delegate_Imp0);
}
if (type == typeof(UnityEngine.Events.UnityAction))
{
return new UnityEngine.Events.UnityAction(__Gen_Delegate_Imp0);
}
if (type == typeof(System.Func))
{
return new System.Func(__Gen_Delegate_Imp1);
}
...
return null;
}
类似地,如果引用类型为interface,则需要考虑是否是lua table映射到interface的情况,如果是则需要返回包装好lua table的interface。包装类的字段对应table中的字段,方法则对应table中所包含的function。包装类也是自动生成的,在XLuaGenAutoRegister这个文件里:
static void Init(LuaEnv luaenv, ObjectTranslator translator)
{
translator.AddInterfaceBridgeCreator(typeof(System.Collections.IEnumerator), SystemCollectionsIEnumeratorBridge.__Create);
translator.AddInterfa服务器托管网ceBridgeCreator(typeof(XLuaTest.IExchanger), XLuaTestIExchangerBridge.__Create);
translator.AddInterfaceBridgeCreator(typeof(Tutorial.CSCallLua.ItfD), TutorialCSCallLuaItfDBridge.__Create);
translator.AddInterfaceBridgeCreator(typeof(XLuaTest.InvokeLua.ICalc), XLuaTestInvokeLuaICalcBridge.__Create);
}
除此之外,xlua对于一般的引用对象,还支持更灵活的方式,如果lua栈上的值为table,则会自动地通过反射,获取C#对象的field info,根据field name获取table中的值,然后再根据field type调用合适的get函数填充C#对象的field value。
foreach (FieldInfo field in type.GetFields())
{
LuaAPI.xlua_pushasciistring(L, field.Name);
LuaAPI.lua_rawget(L, idx);
if (!LuaAPI.lua_isnil(L, -1))
{
try
{
field.SetValue(obj, GetCaster(field.FieldType)(L, n + 1,
target == null || field.FieldType.IsPrimitive() || field.FieldType == typeof(string) ? null : field.GetValue(obj)));
}
catch (Exception e)
{
throw new Exception("exception in tran " + field.Name + ", msg=" + e.Message);
}
}
LuaAPI.lua_pop(L, 1);
}
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
STM32——感应开关盖垃圾桶 1.定时器介绍 软件定时 缺点:不精确、占用CPU资源 void Delay500ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 4; j = 129; k = …