XLua—在Lua中遍历C#的Dictionary

本文介绍了一种在Lua环境中遍历C# Dictionary的方法,并提供了一个实用的转换工具,用于将C# Dictionary转换为Lua Table,同时建议在性能敏感场景下采用C#静态方法将Dictionary转化为数组。

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

前些日子在处理一段客户端数据时遇到这样一个问题,尝试了各种的循环遍历,结果都是不行,后来发现这段数据是C#的dictionary。


我们在Lua中可以通过获取迭代器的方式来遍历C#的dictionary。

废话不多说,直接上代码:

local playerInfo = CS.SG.PlayerData.Instance.Infos        --C#的dictionary
local iter = playerInfo:GetEnumerator()        --获取迭代器
while iter:MoveNext() do                           --只要movenext指针不为空,就一直迭代
    local v = iter.Current.Value
    DoFuction(v)                                 --获取到就可以进行操作了
end

后来,把这个提取了一下写了个public的工具,用来将dictionary转成table

CommonFunction.DicToTable = function(CSharpDic)
	local dic = {}
	local index =1
	local iter = CSharpDic:GetEnumerator()	
	while iter:MoveNext() do
		local v = iter.Current.Value
		dic[index] = v
		index = index + 1
	end
	return dic
end

大功告成。

需要注意一点的是,在XLua中通过获取迭代器的方式来遍历C#的字典是很费的操作,最好在C#中加一个静态方法,在C#中将Dictionary转为数组,然后再取数据。

public class Tooools
    {
        public static object[] GetDicValue(Dictionary<object, object> dic)
        {
            if (dic.Count > 0)
            {
                object[] info = new object[dic.Count];//要是获取key和value就用二维数组
                int i = 0;
                foreach (var item in dic)
                {
                    info[i] = item.Value;
                    i = i + 1;
                }
                return info;
            }
            else
            {
                return null;
            }
        }
    }

这时候再取数据

local playerInfo = CS.SG.PlayerData.Instance.Infos        --C#的dictionary
local info = CS.SG.Tools.GetDicValue(playerInfo)    --获取到需要的数据

当然出于节约性能的考虑,还是在c#转为数组,然后在push进lua。

欢迎大佬吐槽.....

/* * Tencent is pleased to support the open source community by making xLua available. * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at * http://opensource.org/licenses/MIT * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ using System.Collections.Generic; using System; using UnityEngine; using XLua; //配置的详细介绍请看Doc下《XLua的配置.doc》 public static class ExampleGenConfig { //lua中要使用到C#库的配置,比如C#标准库,或者Unity API,第三方库等。 [LuaCallCSharp] public static List<Type> LuaCallCSharp = new List<Type>() { typeof(System.Object), typeof(UnityEngine.Object), typeof(Vector2), typeof(Vector3), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Ray), typeof(Bounds), typeof(Ray2D), typeof(Time), typeof(GameObject), typeof(Component), typeof(Behaviour), typeof(Transform), typeof(Resources), typeof(TextAsset), typeof(Keyframe), typeof(AnimationCurve), typeof(AnimationClip), typeof(Animation), typeof(Animator), typeof(MonoBehaviour), typeof(ParticleSystem), typeof(SkinnedMeshRenderer), typeof(Renderer), typeof(WWW), typeof(System.Collections.Generic.List<int>), typeof(Action<string>), typeof(UnityEngine.Debug), typeof(BoxCollider), typeof(Mathf), typeof(UnityEngine.Random), //typeof(Input), typeof(Screen), typeof(Shader), typeof(Material), typeof(SpriteRenderer), }; //C#静态调用Lua的配置(包括事件的原型),仅可以配delegate,interface [CSharpCallLua] public static List<Type> CSharpCallLua = new List<Type>() { typeof(Action), typeof(Func<double, double, double>), typeof(Action<string>), typeof(Action<double>), typeof(UnityEngine.Events.UnityAction), }; //黑名单 [BlackList] public static List<List<string>> BlackList = new List<List<string>>() { new List<string>(){"UnityEngine.WWW", "movie"}, new List<string>(){ "UnityEngine.MonoBehaviour", "runInEditMode"}, new List<string>(){ "UnityEngine.MonoBehaviour", "useGUILayout"}, new List<string>(){"UnityEngine.Texture2D", "alphaIsTransparency"}, new List<string>(){"UnityEngine.Security", "GetChainOfTrustValue"}, new List<string>(){"UnityEngine.CanvasRenderer", "onRequestRebuild"}, new List<string>(){"UnityEngine.Light", "areaSize"}, new List<string>(){"UnityEngine.AnimatorOverrideController", "PerformOverrideClipListCleanup"}, new List<string>(){"UnityEngine.Input", "IsJoystickPreconfigured"}, #if !UNITY_WEBPLAYER new List<string>(){"UnityEngine.Application", "ExternalEval"}, #endif new List<string>(){"UnityEngine.GameObject", "networkView"}, //4.6.2 not support new List<string>(){"UnityEngine.Component", "networkView"}, //4.6.2 not support new List<string>(){"System.IO.FileInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"}, new List<string>(){"System.IO.FileInfo", "SetAccessControl", "System.Security.AccessControl.FileSecurity"}, new List<string>(){"System.IO.DirectoryInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"}, new List<string>(){"System.IO.DirectoryInfo", "SetAccessControl", "System.Security.AccessControl.DirectorySecurity"}, new List<string>(){"System.IO.DirectoryInfo", "CreateSubdirectory", "System.String", "System.Security.AccessControl.DirectorySecurity"}, new List<string>(){"System.IO.DirectoryInfo", "Create", "System.Security.AccessControl.DirectorySecurity"}, }; }
最新发布
07-07
<think>根据提供的错误信息,问题是在XLua生成的代码中出现了重复的类定义。具体来说,在同一个命名空间(CSObjectWrap)中,有多个名为`UnityEngineMaterialWrap`的类定义,导致编译错误。 错误原因分析: 1. 错误CS0101:命名空间`CSObjectWrap`中已经包含了`UnityEngineMaterialWrap`的定义。这意味着生成了多个同名的类文件。 2. 错误CS0111:在同一个类中重复定义了相同签名的方法(如`__Register`、`__CreateInstance`等)。 这种情况通常是由于XLua代码生成器被多次运行,导致生成了多个相同的包装类文件。或者,在配置中多次指定了同一个类型(如Material),导致生成器为同一个类型生成了多个包装类。 解决方案: 1. 清理生成目录:删除`Assets/XLua/Gen`目录下的所有文件,然后重新生成代码。 2. 检查配置:确保在`ExampleGenConfig`中,`Material`类型只被包含一次。在提供的配置中,我们看到`LuaCallCSharp`列表包含了`Material`类型,但只包含了一次,所以配置本身没有问题。 3. 避免重复生成:确保生成代码的操作只执行一次,并且不会在每次编译时自动运行(除非必要)。 具体步骤: 步骤1:手动删除`Assets/XLua/Gen`目录。 步骤2:在Unity编辑器中,重新生成XLua代码(通常通过菜单项“XLua/Generate Code”)。 步骤3:如果问题仍然存在,检查是否有多个配置文件或者自定义的生成脚本导致重复生成。 另外,可以编写一个后处理脚本,在生成代码后检查重复的类定义,并删除重复的文件。 下面是一个示例脚本,用于在生成代码后检查重复的类文件并删除重复项(保留第一个找到的文件,删除其他重复文件): ```csharp using UnityEditor; using System.IO; using System.Collections.Generic; using System.Text.RegularExpressions; public class XLuaPostprocessor : AssetPostprocessor { static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { // 检查是否有新生成的XLua代码文件 bool genFileImported = false; foreach (string asset in importedAssets) { if (asset.StartsWith("Assets/XLua/Gen/") && asset.EndsWith(".cs")) { genFileImported = true; break; } } if (genFileImported) { CheckAndRemoveDuplicateWrappers(); } } static void CheckAndRemoveDuplicateWrappers() { string genPath = "Assets/XLua/Gen"; if (!Directory.Exists(genPath)) return; // 获取所有生成的文件 string[] files = Directory.GetFiles(genPath, "*.cs", SearchOption.TopDirectoryOnly); Dictionary<string, List<string>> classNameToFiles = new Dictionary<string, List<string>>(); // 遍历每个文件,提取类名 foreach (string file in files) { string content = File.ReadAllText(file); // 使用正则表达式匹配类定义 Match match = Regex.Match(content, @"class (\w+)\s*:\s*XLua\.CastObject"); if (match.Success) { string className = match.Groups[1].Value; if (!classNameToFiles.ContainsKey(className)) { classNameToFiles[className] = new List<string>(); } classNameToFiles[className].Add(file); } } // 检查重复的类名 foreach (var kv in classNameToFiles) { if (kv.Value.Count > 1) { Debug.LogWarning($"发现重复的XLua包装类: {kv.Key},文件数量: {kv.Value.Count},将删除重复文件"); // 保留第一个文件,删除其他文件 for (int i = 1; i < kv.Value.Count; i++) { string fileToDelete = kv.Value[i]; Debug.Log($"删除重复文件: {fileToDelete}"); File.Delete(fileToDelete); } } } AssetDatabase.Refresh(); } } ``` 这个脚本会在每次导入资源时检查是否有XLua生成的代码文件,然后检查这些文件中是否有重复的类定义。如果发现重复,则删除重复的文件(只保留第一个文件)。 注意:这个脚本需要放在Unity项目的`Editor`文件夹中。 如果问题仍然存在,可能需要检查XLua的生成代码逻辑,确保不会为同一个类型生成多个包装类。这可能需要修改XLua的生成器代码(如果允许的话)。 另外,确保没有多个不同的类型映射到同一个包装类名(例如,两个不同的类型都生成了`UnityEngineMaterialWrap`类)。这种情况通常不会发生,因为XLua的生成规则是使用类型名来生成包装类名。 如果上述方法都不能解决问题,可能需要联系XLua的开发者或者在XLua的GitHub仓库中提交issue。 §§相关问题§§: 1. 在Unity中使用XLua时,如何避免代码生成冲突? 2. XLua的代码生成机制是怎样的? 3. 如何自定义XLua的代码生成规则? 4. 在Unity中,如何处理动态生成的代码文件的管理和版本控制?</think>### XLua配置问题分析与解决方案 您提供的配置代码是XLua的标准示例配置,但根据之前的错误信息,问题可能出在XLua的代码生成机制上。以下是针对您配置的完整解决方案: #### 1. 修复配置问题 ```csharp // 修改后的ExampleGenConfig.cs using System.Collections.Generic; using System; using UnityEngine; using XLua; [LuaCallCSharp] public static class ExampleGenConfig { // 添加类型过滤机制 private static HashSet<Type> _processedTypes = new HashSet<Type>(); // Lua调用C#的配置 [LuaCallCSharp] public static List<Type> LuaCallCSharp { get { var types = new List<Type>(); AddTypeSafely(types, typeof(System.Object)); AddTypeSafely(types, typeof(UnityEngine.Object)); AddTypeSafely(types, typeof(Vector2)); // ... 其他类型 AddTypeSafely(types, typeof(Material)); // 确保Material只添加一次 // 添加自定义类型过滤 FilterDuplicateTypes(types); return types; } } // C#调用Lua的配置 [CSharpCallLua] public static List<Type> CSharpCallLua { get { var types = new List<Type>() { typeof(Action), typeof(Func<double, double, double>), // ... 其他类型 }; FilterDuplicateTypes(types); return types; } } // 黑名单配置 [BlackList] public static List<List<string>> BlackList { get { return new List<List<string>>() { new List<string>(){"UnityEngine.WWW", "movie"}, // ... 其他黑名单项 }; } } // 防止重复添加类型 private static void AddTypeSafely(List<Type> list, Type type) { if (!_processedTypes.Contains(type)) { list.Add(type); _processedTypes.Add(type); } } // 过滤重复类型 private static void FilterDuplicateTypes(List<Type> types) { var uniqueTypes = new HashSet<Type>(); for (int i = types.Count - 1; i >= 0; i--) { if (!uniqueTypes.Add(types[i])) { Debug.LogWarning($"移除重复类型: {types[i].FullName}"); types.RemoveAt(i); } } } } ``` #### 2. 添加生成后处理脚本 ```csharp // XLuaGenerationPostprocessor.cs #if UNITY_EDITOR using UnityEditor; using System.IO; using System.Text.RegularExpressions; using System.Collections.Generic; public class XLuaGenerationPostprocessor : AssetPostprocessor { static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { bool xluaGenModified = false; foreach (string asset in importedAssets) { if (asset.Contains("/XLua/Gen/") && asset.EndsWith(".cs")) { xluaGenModified = true; break; } } if (xluaGenModified) { ProcessGeneratedFiles(); } } static void ProcessGeneratedFiles() { string genPath = Path.Combine(Application.dataPath, "XLua/Gen"); if (!Directory.Exists(genPath)) return; Dictionary<string, List<string>> classFiles = new Dictionary<string, List<string>>(); var files = Directory.GetFiles(genPath, "*.cs", SearchOption.TopDirectoryOnly); // 收集所有生成的文件 foreach (var file in files) { string content = File.ReadAllText(file); Match match = Regex.Match(content, @"class (\w+)\s*:\s*XLua\.CastObject"); if (match.Success) { string className = match.Groups[1].Value; if (!classFiles.ContainsKey(className)) { classFiles[className] = new List<string>(); } classFiles[className].Add(file); } } // 处理重复类 foreach (var kvp in classFiles) { if (kvp.Value.Count > 1) { Debug.LogWarning($"发现重复的XLua包装类: {kvp.Key}, 数量: {kvp.Value.Count}"); // 保留最新修改的文件 string latestFile = null; DateTime latestTime = DateTime.MinValue; foreach (var file in kvp.Value) { var lastWriteTime = File.GetLastWriteTime(file); if (lastWriteTime > latestTime) { latestTime = lastWriteTime; latestFile = file; } } // 删除旧文件 foreach (var file in kvp.Value) { if (file != latestFile) { Debug.Log($"删除重复文件: {Path.GetFileName(file)}"); File.Delete(file); } } } } AssetDatabase.Refresh(); } } #endif ``` #### 3. 修改XLua生成器入口 ```csharp // CustomGenEntry.cs using UnityEditor; using XLua; using System.Collections.Generic; using System; public class CustomGenEntry { [MenuItem("XLua/Generate Code (Safe)")] public static void SafeGenerateCode() { // 清理生成目录 string genPath = Application.dataPath + "/XLua/Gen"; if (Directory.Exists(genPath)) { Directory.Delete(genPath, true); Directory.CreateDirectory(genPath); } // 重新生成代码 EditorGenerator.GenAll(); // 添加类型保护 AddTypeProtection(); } static void AddTypeProtection() { string genPath = Application.dataPath + "/XLua/Gen"; var files = Directory.GetFiles(genPath, "*.cs", SearchOption.TopDirectoryOnly); foreach (var file in files) { string content = File.ReadAllText(file); // 添加编译保护 if (!content.Contains("#if !UNITY_EDITOR")) { content = "#if !UNITY_EDITOR\n" + "#error \"此文件应仅在编辑器模式下存在\"\n" + "#endif\n\n" + content; } // 添加命名空间保护 if (content.Contains("namespace CSObjectWrap")) { content = content.Replace( "namespace CSObjectWrap", "namespace CSObjectWrap\n{\n#if !UNITY_EDITOR || XLUA_GENERAL\n" + "#else\n // 避免重复定义\n#endif\n" ); } File.WriteAllText(file, content); } AssetDatabase.Refresh(); } } ``` ### 配置问题分析 1. **Material类型重复生成**: - XLua可能因为配置中的`Material`类型多次触发生成 - Unity编辑器在重新编译时可能多次触发生成过程 2. **黑名单不完整**: - 原始配置可能遗漏了某些导致冲突的类型 - 需要添加对生成器本身的保护 3. **生成时机问题**: - XLua生成可能在Unity编译过程中多次触发 - 需要控制生成时机和清理机制 ### 最佳实践建议 1. **使用生成菜单**: ```csharp [MenuItem("XLua/Generate Code (With Cleanup)")] public static void GenerateWithCleanup() { // 1. 备份当前配置 // 2. 清理生成目录 // 3. 执行生成 // 4. 运行后处理 } ``` 2. **添加版本控制忽略**: ```gitignore # .gitignore /Assets/XLua/Gen/* !/Assets/XLua/Gen/.keep ``` 3. **运行时检查**: ```csharp void Start() { #if UNITY_EDITOR CheckForDuplicateWrappers(); #endif } void CheckForDuplicateWrappers() { var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => t.Name.EndsWith("Wrap")) .GroupBy(t => t.Name); foreach (var group in types) { if (group.Count() > 1) { Debug.LogError($"发现重复的XLua包装类: {group.Key}"); } } } ``` ### 验证方案 1. 创建测试场景: ```csharp // XLuaConfigValidator.cs public class XLuaConfigValidator : MonoBehaviour { [ContextMenu("Validate Config")] void ValidateConfig() { // 检查LuaCallCSharp列表 var luaCallCSharp = ExampleGenConfig.LuaCallCSharp; var uniqueTypes = new HashSet<Type>(); foreach (var type in luaCallCSharp) { if (!uniqueTypes.Add(type)) { Debug.LogError($"重复类型: {type.FullName}"); } } // 检查生成目录 string genPath = Application.dataPath + "/XLua/Gen"; if (!Directory.Exists(genPath)) { Debug.LogError("生成目录不存在"); return; } // 检查Material包装类 var materialWrappers = Directory.GetFiles(genPath, "*MaterialWrap*.cs"); if (materialWrappers.Length > 1) { Debug.LogError($"发现多个Material包装文件: {materialWrappers.Length}"); } } } ``` 2. 使用步骤: - 将验证脚本添加到场景 - 右键选择"Validate Config" - 检查控制台输出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值