|
上一节我们讨论了如安在lua层扩展担任C#类的机制。本节我们来看一下tolua的反射机制,它主要用于在lua层访谒没有事先wrap过的C#类对象和方式。主要实现的思想就是操作C#自带的反射机制,将反射相关的函数注册到lua层。此次的例子是example 22,同样我们主要不雅察看例子中的lua代码:
require 'tolua.reflection'
tolua.loadassembly('Assembly-CSharp')
local BindingFlags = require 'System.Reflection.BindingFlags'
function DoClick()
print('do click')
end
function Test()
local t = typeof('TestExport')
local func = tolua.getmethod(t, 'TestReflection')
func:Call()
func:Destroy()
func = nil
local objs = {Vector3.one, Vector3.zero}
local array = tolua.toarray(objs, typeof(Vector3))
local obj = tolua.createinstance(t, array)
local constructor = tolua.getconstructor(t, typeof(Vector3):MakeArrayType())
local obj2 = constructor:Call(array)
constructor:Destroy()
func = tolua.getmethod(t, 'Test', typeof('System.Int32'):MakeByRefType())
local r, o = func:Call(obj, 123)
print(r..':'..o)
func:Destroy()
local property = tolua.getproperty(t, 'Number')
local num = property:Get(obj, null)
print('object Number: '..num)
property:Set(obj, 456, null)
num = property:Get(obj, null)
property:Destroy()
print('object Number: '..num)
local field = tolua.getfield(t, 'field')
num = field:Get(obj)
print('object field: '.. num)
field:Set(obj, 2048)
num = field:Get(obj)
field:Destroy()
print('object field: '.. num)
field = tolua.getfield(t, 'OnClick')
local onClick = field:Get(obj)
onClick = onClick + DoClick
field:Set(obj, onClick)
local click = field:Get(obj)
click:DynamicInvoke()
field:Destroy()
click:Destroy()
end这个例子可以分为好几个部门,拆开来看,首先是反射调用一个无参的C#静态函数TestReflection,它的实现如下:
public sealed class TestExport
{
public static void TestReflection()
{
Debugger.Log(”call TestReflection()”);
}
}
相关的lua代码如下:
local t = typeof('TestExport')
local func = tolua.getmethod(t, 'TestReflection')
func:Call()
func:Destroy()
func = nil全局的typeof定义在typeof.lua中,如果传入的参数是string,会先在type缓存中查找,查找不到则会调用到C#层的FindType函数,它会从C#的Assembly中查找到对应的Type,然后作为userdata压入到lua层,也就是第1行t的值是一个userdata。
第2行的tolua.getmethod会调用到C#层的GetMethod函数,该函数将userdata转换为对应的Type,然后操作反射获取到对应的MethodInfo。为了把反射相关的信息一股脑儿地告诉了lua层,这里引入了一个名为LuaMethod的类,它代表一个C#的反射函数,包含class,method,以及参数类型列表信息:
public sealed class LuaMethod
{
MethodInfo method = null;
List<Type> list = new List<Type>();
Type kclass = null;
[NoToLuaAttribute]
public LuaMethod(MethodInfo md, Type t, Type[] types)
{
method = md;
kclass = t;
if (types != null)
{
list.AddRange(types);
}
}
}
所以,这里第2行得到的func是一个代表LuaMethod对象的userdata。那么第3行实际上调用到的就是LuaMethod类的Call方式,它其实就是对C#反射调用方式的过程做了一层封装。由于这里反射的函数是静态无参而且没有返回值的,因此lua层并不需要提供额外的参数给C#,也不需要从C#层获取返回值信息。
然后来看一下lua层如何反射调用C#的构造函数,这里给出了两种方式,首先看一下第一种:
local objs = {Vector3.one, Vector3.zero}
local array = tolua.toarray(objs, typeof(Vector3))
local obj = tolua.createinstance(t, array)它实际调用到的是C#层参数为Vector3数组的构造函数:
public sealed class TestExport
{
public TestExport(Vector3[] v)
{
Debugger.Log(”call TestExport(params Vector3[] v)”);
}
}
这里lua代码第2行也调用了全局的typeof,而这里传入的不是string,Vector3我们之前提到过是lua层本身实现的一个table,对于table类型来说,如果type缓存中不存在,则调用的是C#的GetClassType:
static int GetClassType(IntPtr L)
{
int reference = LuaDLL.tolua_getmetatableref(L, 1);
if (reference > 0)
{
Type t = LuaStatic.GetClassType(L, reference);
Push(L, t);
}
else
{
int ret = LuaDLL.tolua_getvaluetype(L, -1);
if (ret != LuaValueType.None)
{
Type t = TypeChecker.LuaValueTypeMap[ret];
Push(L, t);
}
else
{
Debugger.LogError(”type not register to lua”);
LuaDLL.lua_pushnil(L);
}
}
return 1;
}
当然,由于这里的Vector3是lua本身实现的类型,所以C#层压根找不到对应的meta reference,因此还是要回到lua层通过GetLuaValueType查找,再通过LuaValueTypeMap映射到C#的Type。tolua.toarray所做的事情就是将lua层的table转换成C#的Array,对应的C#函数为TableToArray。那么,第2行执行结束得到的array是一个暗示C#层Array的userdata。类似地,第3行的tolua.createinstance就是对C#的Activator.CreateInstance进行了一层封装,将转换后的Type类型和构造函数的参数传递进去。
再看下第二种:
local constructor = tolua.getconstructor(t, typeof(Vector3):MakeArrayType())
local obj2 = constructor:Call(array)由上文可知,typeof(Vector3)返回的是C#的Type类型,代表C#的Vector3,那么调用MakeArrayType得到的就是Vector3[]的Type类型。tolua.getconstructor实际调用的是C#层的GetConstructor:
static int GetConstructor(IntPtr L)
{
try
{
int count = LuaDLL.lua_gettop(L);
Type t = (Type)ToLua.CheckObject(L, 1, typeof(Type));
Type[] types = null;
if (count > 1)
{
types = new Type[count - 1];
for (int i = 2; i <= count; i++)
{
Type ti = ToLua.CheckMonoType(L, i);
if (ti == null) LuaDLL.luaL_typerror(L, i, ”Type”);
types[i - 2] = ti;
}
}
ConstructorInfo ret = t.GetConstructor(types);
PushLuaConstructor(L, ret, types);
}
catch (Exception e)
{
return LuaDLL.toluaL_exception(L, e);
}
return 1;
}
这个函数实际上也是对Type.GetConstructor进行了封装。得到ConstructorInfo之后,为了把它和构造函数的参数列表类型信息关联起来,这里引入了一个LuaConstructor的辅助类来辅佐做这件事情:
public sealed class LuaConstructor
{
ConstructorInfo method = null;
List<Type> list = null;
[NoToLuaAttribute]
public LuaConstructor(ConstructorInfo func, Type[] types)
{
method = func;
if (types != null)
{
list = new List<Type>(types);
}
}
}
lua层得到的constructor就是代表这个LuaConstructor类型的userdata。类似地,它的Call方式也是对ConstructorInfo.Invoke进行了封装,同时会对lua层传入的数据进行类型查抄。
例子中的下一个部门是关于ref/out类型的:
func = tolua.getmethod(t, 'Test', typeof('System.Int32'):MakeByRefType())
local r, o = func:Call(obj, 123)
func:Destroy()这个和前面类似,这里就不展开了,它反射的对应C#方式为:
public int Test(out int i)
{
i = 1024;
Debugger.Log(”call Test(ref int i)”);
return 3;
}
下一部门是通过反射获取property的:
local property = tolua.getproperty(t, 'Number')
local num = property:Get(obj, null)
property:Set(obj, 456, null)
num = property:Get(obj, null)
property:Destroy()按照之前的分析,这里我们也很容易猜出getproperty返回的是一个LuaProperty辅助类,它包含了PropertyInfo和Type信息,Type主要是为了在lua层传入参数时进行类型查抄:
public sealed class LuaProperty
{
PropertyInfo property = null;
Type kclass = null;
[NoToLuaAttribute]
public LuaProperty(PropertyInfo prop, Type t)
{
property = prop;
kclass = t;
}
}
它反射的对应C#属性为:
public int Number
{
get
{
return number;
}
set
{
number = value;
}
}
例子的最后一部门,是field相关的:
local field = tolua.getfield(t, 'field')
num = field:Get(obj)
print('object field: '.. num)
field:Set(obj, 2048)
num = field:Get(obj)
field:Destroy()
print('object field: '.. num)
field = tolua.getfield(t, 'OnClick')
local onClick = field:Get(obj)
onClick = onClick + DoClick
field:Set(obj, onClick)
local click = field:Get(obj)
click:DynamicInvoke()
field:Destroy()
click:Destroy()同样地,这里也有一个LuaField的辅助类,帮我们保留着FieldInfo和Type信息:
public sealed class LuaField
{
FieldInfo field = null;
Type kclass = null;
[NoToLuaAttribute]
public LuaField(FieldInfo info, Type t)
{
field = info;
kclass = t;
}
}
这里反射的对应C#成员为:
public int field = 1024;
public System.Action OnClick = delegate { };
下一节我们将探讨,如安在tolua中传递布局体,以及新增一个struct类型,需要做哪些工作。
如果你感觉我的文章有辅佐,欢迎存眷我的微信公众号我是真的想做游戏啊 |
|