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

本文介绍在Lua中如何遍历C#的List和Dictionary对象,提供了多种遍历方法并详细解释了pairs和ipairs的区别。
部署运行你感兴趣的模型镜像

这一篇主要讲讲如何在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,中断循环)


您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

在 Unity 中使用 XLua 进行 C# 与 Lua 的交互时,传递数组 `List<T>` 类型的数据是一个常见需求。XLua 提供了灵活的机制,可以通过 Lua 的 table 来接收 C# 侧传递的数组列表数据。以下是具体的实现方式使用方法。 ### 1. C# 侧定义并传递数组 List 数据 在 C# 中,可以通过调用 Lua 函数并传递数组 `List<T>` 类型的数据。XLua 会自动将这些数据转换为 Lua 中的 table 类型。 #### 示例:传递数组 ```csharp using XLua; public class LuaCaller : MonoBehaviour { private LuaEnv luaEnv = new LuaEnv(); void Start() { // 加载 Lua 脚本 luaEnv.DoString("require 'TestLua'"); // 定义一个 C# 数组 int[] numbers = new int[] { 1, 2, 3, 4, 5 }; // 调用 Lua 函数并传递数组 Action<int[]> luaFunction = luaEnv.Global.Get<Action<int[]>>("ReceiveArray"); luaFunction(numbers); } void OnDestroy() { luaEnv.Dispose(); } } ``` #### 示例:传递 List ```csharp void Start() { // 加载 Lua 脚本 luaEnv.DoString("require 'TestLua'"); // 定义一个 C# List List<string> names = new List<string> { "Alice", "Bob", "Charlie" }; // 调用 Lua 函数并传递 List Action<List<string>> luaFunction = luaEnv.Global.Get<Action<List<string>>>>("ReceiveList"); luaFunction(names); } ``` ### 2. Lua 侧接收并处理数组 List 数据 在 Lua 脚本中,接收到的数据会被自动转换为 table 类型,可以直接使用 Lua 的 table 操作方法进行处理。 #### 示例:接收数组 ```lua -- TestLua.lua function ReceiveArray(arr) for i, v in ipairs(arr) do print("Array element " .. i .. ": " .. v) end end ``` #### 示例:接收 List ```lua function ReceiveList(list) for i, v in ipairs(list) do print("List element " .. i .. ": " .. v) end end ``` ### 3. 传递复杂对象数组 List 如果传递的是自定义类的数组 `List<T>`,需要在 C# 类上添加 `[LuaCallCSharp]` 属性,以便 XLua 能够正确生成绑定代码。 #### 示例:传递对象数组 ```csharp [LuaCallCSharp] public class Person { public string Name; public int Age; } void Start() { Person[] people = new Person[] { new Person { Name = "Alice", Age = 25 }, new Person { Name = "Bob", Age = 30 } }; Action<Person[]> luaFunction = luaEnv.Global.Get<Action<Person[]>>("ReceivePeople"); luaFunction(people); } ``` #### Lua 侧处理对象数组 ```lua function ReceivePeople(people) for i, person in ipairs(people) do print("Person " .. i .. ": " .. person.Name .. ", " .. person.Age) end end ``` ### 4. 性能优化与注意事项 - **避免频繁的垃圾回收**:在频繁调用 Lua 函数时,尽量复用 LuaEnv 实例,避免频繁创建销毁。 - **类型匹配**:确保 Lua 侧的函数参数类型与 C# 侧传递的数据类型一致,否则会导致运行时错误。 - **性能瓶颈**:对于大量数据的传递,建议使用原生数组而非 `List<T>`,因为数组的内存布局更紧凑,转换效率更高 [^3]。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值