XLua 遍历C# List或Dictionary元素以及pairs和ipairs

本文介绍在Lua中如何遍历C#的List和Dictionary对象,提供了多种遍历方法并详细解释了pairs和ipairs的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一篇主要讲讲如何在lua中遍历List<>或者Dictionary<>的对象。

起因是在XLua官方讨论群里面正好有人问了怎么遍历,然后官方人员说使用for k,v in pairs(要遍历对象) do ... end就可以了。自己就顺手去试了试,结果发现一直报错,纠结了大半天,就在这儿记录一下。最后问了官方的人,结果是因为XLua不是最新的版本,心里苦啊。所有大家一定要更新好XLua,否则可能和我一样会遇到下面这些错误:

 bad argument #1 to 'for iterator' (table expected, got System.Collections.Generic.List`1


遍历

需求

接着就说说如何实现XLua中遍历C#的List或Dictionary,首先我们先定义好需要遍历的测试对象,如下。

public class Fu {
    public List<int> GetList() {
        List<int> l = new List<int>();
        l.Add(1);
        l.Add(3);
        l.Add(7);
        l.Add(6);
        l.Add(0);
        return l;
    }

    public List<string> GetSList() {
        List<string> l = new List<string>();
        l.Add("aa");
        l.Add("sdf");
        l.Add("wer");
        l.Add("rr");
        l.Add("w3");
        return l;
    }

    public Dictionary<int, string> GetDic() {
        Dictionary<int, string> dic = new Dictionary<int, string>();
        dic.Add(100, "ad");
        dic.Add(103, "ewq");
        dic.Add(140, "2s");
        dic.Add(600, "fs");
        return dic;
    }
}

方法一

接着我们即可在Lua中实现对应的遍历,方法都是一样的(for k,v in pairs(list) do then),如下:

--获取对应的List
local fu = CS.Fu();
local list = fu:GetList();
local slist = fu:GetSList();
local dic = fu:GetDic();

--遍历
for k,v in pairs(list) do
	print('List<int>---'..k..'--'..v);
end

print('============================');
for k,v in pairs(slist) do
	print('List<string>---'..k..'--'..v);
end

print('============================');
for k,v in pairs(dic) do
	print('Dictionary<int, string>---'..k..'--'..v);
end

打印的Log如下



方法二

当然除了上述的方法(类似c#的foreach)我们还可以用下面的方法来遍历(类似c#的for):

for i = 0, 4 do
    print(list[i]);
end

方法三

如果XLua版本不升级的也可以在C#端给List,Dictionary做一层封装,将其转换成LuaTable,然后再用第一种方法去遍历。


补充

关于Lua中的for循环以及上诉用到pairs方法,在官方文档都有描述,这里贴一份(Lua5.3)

for

for 有两种形式:一种是数字形式,另一种是通用形式。

数字形式的 for 循环,通过一个数学运算不断地运行内部的代码块。 下面是它的语法:

stat ::= for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end

block 将把 name 作循环变量。 从第一个 exp 开始起,直到第二个 exp 的值为止, 其步长为第三个 exp 。 更确切的说,一个for 循环看起来是这个样子

for v = e1, e2, e3 do block end

这等价于代码:

do
	local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
	if not (var and limit and step) then error() end
	var = var - step
	while true do
		var = var + step
		if (step >= 0 and var > limit) or (step < 0 and var < limit) then
			break
		end
		local v = var
	block
	end
end

注意下面这几点:

1.所有三个控制表达式都只被运算一次, 表达式的计算在循环开始之前。 这些表达式的结果必须是数字。

2.var,limit,以及 step 都是一些不可见的变量。 这里给它们起的名字都仅仅用于解释方便。

3.如果第三个表达式(步长)没有给出,会把步长设为 1 。

4.你可以用 break 和 goto 来退出 for 循环。

5.循环变量 v 是一个循环内部的局部变量; 如果你需要在循环结束后使用这个值, 在退出循环前把它赋给另一个变量。


通用形式的 for 通过一个叫作 迭代器 的函数工作。 每次迭代,迭代器函数都会被调用以产生一个新的值, 当这个值为 nil 时,循环停止。 通用形式的 for 循环的语法如下:

stat ::= for namelist in explist do block end

namelist ::= Name {‘,’ Name}

这样的 for 语句

for var_1, ···, var_n in explist do block end

它等价于这样一段代码:

do
	local f, s, var = explist
	while true do
		local var_1, ···, var_n = f(s, var)
		if var_1 == nil then break end
		var = var_1
		block
	end
end

注意以下几点:

1.explist 只会被计算一次。 它返回三个值, 一个 迭代器 函数, 一个 状态, 一个 迭代器的初始值。

2.f, s,与 var 都是不可见的变量。 这里给它们起的名字都只是为了解说方便。

3.你可以使用 break 来跳出 for 循环。

4.环变量 var_i 对于循环来说是一个局部变量; 你不可以在 for 循环结束后继续使用。 如果你需要保留这些值,那么就在循环跳出或结束前赋值到别的变量里去。


ipairs (t)

返回三个值(迭代函数、表 t 以及 0 ), 如此,以下代码

for i,v in ipairs(t) do body end

将迭代键值对(1,t[1]) ,(2,t[2]), ... ,直到第一个空值。

个人补充:使用这种方式遍历表,会从下标1开始读取对应的值(lua的table下标1起始),然后读取下标2,3...,当读到对应下标的值为nil时,就中断循环。同时忽略其他的下标,例如table['a']。

pairs (t)

如果 t 有元方法 __pairs, 以 t 为参数调用它,并返回其返回的前三个值。

否则,返回三个值:next 函数, 表 t,以及 nil。 因此以下代码

for k,v in pairs(t) do body end

能迭代表 t 中的所有键值对。

个人补充:这种方式遍历表,可以遍历表中所有的键值对。没有ipairs的那些约束,同时这种读取table并不是一种有序的读取。


因为纠结前面的问题的时候顺便也纠结了波pairs和ipairs的区别,注,上述遍历不能使用ipairs否则会报错

List<int>:LuaException: c# exception:System.ArgumentOutOfRangeException: Argument is out of range.

List<string>:LuaException: try to get System.Collections.Generic.List`1[System.String].Item throw a exception:System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentOutOfRangeException: Argument is out of range.

Dictionary:LuaException: try to get System.Collections.Generic.Dictionary`2[System.Int32,System.String].Item throw a exception:System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.


关于两者的区分,直接例子说明(更多情况大家可以举例验证):

local table1 = {
	5,6,8,4
}

for k,v in pairs(table1) do
	print(k..'-'..v);
end
--打印结果为:1-5	2-6		3-8		4-4(有序)


print('============================');
for k,v in ipairs(table1) do
	print(k..'-'..v);
end
--打印结果为:1-5	2-6		3-8		4-4(有序,因为下标1,2,3,4全有,所以全部输出)





local table2 = {
	[1]=5,[3]=6,[4]=8,[5]=4
}


print('============================');
for k,v in pairs(table2) do
	print(k..'-'..v);
end
--打印结果为:4-8	1-5		5-4		3-6(乱序)


print('============================');
for k,v in ipairs(table2) do
	print(k..'-'..v);
end
--打印结果为:1-5(因为当读取下标2的值时为nil,中断循环)





local table3 = {
	['a']=5,['b']=6,['c']=8,['d']=4
}


print('============================');
for k,v in pairs(table3) do
	print(k..'-'..v);
end
--打印结果为:b-6	c-8		d-4		a-5(乱序)


print('============================');
for k,v in ipairs(table3) do
	print(k..'-'..v);
end
--打印结果为:无打印(因为当读取下标1的值时为nil,中断循环)


<think>首先,用户的问题是:“我想了解在XLua框架中Lua脚本与C#代码之间的交互机制 请问XLua中Lua与C#交互的实现原理方法”。我需要基于提供的引用信息来回答。 参考的引用包括: - [^1]: 关于Lua调用C#的方法,如使用[LuaCallCsharp]标签、静态列表等。 - [^2]: 创建C#类并标记方法为[LuaCallCsharp],以便Lua调用。 - [^3]: XLua中Lua与C#交互的机制,包括反射调用、userdata映射等细节。 - [^4]: C#调用Lua时的返回值处理,如使用outref参数。 - [^5]: XLua的HotFix机制,包括StatelessStateful模式。 系统级指令要求: 1. 所有行内数学表达式使用$...$格式,例如$x^2$。但在这个问题中,可能没有数学表达式,所以如果出现,我需要正确使用。 2. 独立公式使用$$...$$并单独成段。 3. LaTeX语法正确。 4. 使用中文回答。 5. 生成相关问题。 6. 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户可见层要求:回答结构清晰,帮助用户逐步解决问题,保证回答真实可靠。 回答结构: - 首先,介绍XLua框架的基本概念。 - 然后,详细解释Lua调用C#的原理方法。 - 接着,解释C#调用Lua的原理方法。 - 最后,总结交互机制。 在回答中,引用相关信息时添加引用标识。 生成相关问题:在回答结束时,添加“§§相关问题§§”部分,列出相关问题。 内容大纲: 1. **引言**:简要介绍XLua框架,说明其重要性。 2. **Lua调用C#的实现原理方法**: - 使用[LuaCallCsharp]标签生成适配代码。 - 对于系统API,使用静态列表。 - 原理:基于反射动态生成代码接口。 - 引用[^1][^2]。 - 详细解释userdata映射反射调用过程(引用[^3])。 3. **C#调用Lua的实现原理方法**: - 返回值处理:C#只能接收第一个返回值,后续返回值用outref参数接收(引用[^4])。 - 可能涉及HotFix机制(引用[^5])。 4. **交互机制的总结**:强调XLua的优点,如无需频繁生成代码。 5. **相关问题**:生成3-5个相关问题。 确保回答清晰、逐步。 最终回答以中文呈现。</think>### XLua框架中Lua与C#交互机制详解 XLua是一个强大的Unity插件,用于实现Lua脚本与C#代码之间的高效交互。它在游戏开发中广泛应用,支持热更新、脚本扩展等功能。其核心机制基于动态代码生成反射技术,避免了频繁的代码编译。下面我将逐步解释XLua中Lua与C#交互的实现原理方法,确保回答结构清晰、易于理解。 #### 1. Lua调用C#的实现原理方法 在XLua中,Lua脚本调用C#代码主要通过以下方式实现,其原理涉及动态适配反射机制。 - **方法一:使用[LuaCallCsharp]标签生成适配代码** 当C#方法需要被Lua调用时,开发者可以在C#代码中添加[LuaCallCsharp]标签。XLua会自动生成适配代码,将C#对象映射到Lua的userdata类型。例如: ```csharp using UnityEngine; using XLua; [LuaCallCSharp] public class MyCSharpClass { public static void MyMethod(string message) { Debug.Log("Received message from Lua: " + message); } } ``` 在Lua脚本中,可以直接调用该方法: ```lua MyCSharpClass.MyMethod("Hello from Lua") ``` 原理:XLuaC#对象映射到Lua的userdata,userdata仅保留对象在C#侧objects_pool的索引。当Lua调用时,XLua通过反射获取MethodInfo,动态构造委托(delegate)来处理参数传递结果返回[^1][^2]。 - **方法二:使用静态列表处理系统API** 如果C#类是系统API(如Unity的GameObject),无法直接添加标签,则需使用静态列表。开发者创建一个静态类,列出目标类型: ```csharp public static List<Type> mymodule_LuaCallCS_List = new List<Type>() { typeof(GameObject), typeof(Dictionary<string, int>) }; ``` 这样,XLua能在运行时通过反射动态生成接口,无需修改源代码[^1]。 - **原理细节**: - **映射机制**:C#对象被映射为Lua的userdata,userdata的metatable(元表)设置__index方法。当Lua访问属性方法时,XLua根据对象类型方法名,反射获取MethodInfo,并构造委托压栈。 - **参数处理**:调用时,XLua从Lua栈中取出参数,转换为C#类型;结果返回时,再将C#对象压回Lua栈。 - **优化**:XLua依赖编译器环境动态生成代码,减少频繁代码生成,提升效率[^3][^5]。 注意:添加新方法后,如果已生成适配代码,需重新生成删除旧代码,否则会引发Lua异常(如“视图访问一个nil的方法”)[^1]。 #### 2. C#调用Lua的实现原理方法 C#调用Lua脚本时,主要通过XLua的API实现,处理返回值时需考虑Lua的多返回值特性。 - **基本调用方法**: 在C#中,使用XLua的`LuaEnv`类执行Lua脚本调用Lua函数。例如: ```csharp LuaEnv luaEnv = new LuaEnv(); luaEnv.DoString("lua_script = function(a, b) return a + b, a * b end"); // 调用Lua函数 object[] results = luaEnv.Global.Get<LuaFunction>("lua_script").Call(2, 3); ``` 这里,`Call`方法返回一个数组,包含Lua函数的所有返回值。 - **返回值处理**: Lua函数可返回多个值,但C#默认只接收第一个返回值。后续返回值需用`out``ref`参数显式接收: ```csharp int sum; int product; luaEnv.Global.Get<LuaFunction>("lua_script").Call(out sum, out product, 2, 3); Debug.Log($"Sum: {sum}, Product: {product}"); ``` 原理:XLua在底层通过栈操作实现。C#调用时,参数被压入Lua栈;Lua执行后,结果从栈中取出并转换[^4]。 - **HotFix机制**: XLua支持热修复(HotFix),允许在运行时修改C#代码。分为两种模式: - **Stateless模式**:C#对象直接作为hotfix函数的第一个参数(self)。无需额外处理,适合无状态操作。 - **Stateful模式**:在hotfix构造函数中返回一个Lua table,其他函数的self参数为该table。提供更灵活的上下文管理[^5]。 #### 3. 交互机制总结 XLua的交互机制基于动态代码生成反射: - **优点**:无需每次修改都重新生成代码,提升开发效率;支持热更新,适用于游戏逻辑热修复。 - **核心原理**:Lua与C#交互时,通过userdata映射栈操作实现数据传递。反射用于动态获取方法信息,委托处理调用逻辑。 - **性能考虑**:反射调用可能引入开销,但XLua通过缓存优化减少影响。在实际开发中,Lua调用C#更常见,因其便于脚本控制游戏逻辑[^1][^3]。 总之,XLua通过灵活的标签系统反射机制,实现了高效的双向交互。开发者应遵循最佳实践,如及时更新适配代码,以避免运行时错误。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值