C#在foreach循环中修改List元素的可行解决方案求助
你遇到的是C#中foreach遍历的枚举器限制——枚举器会跟踪集合的版本,遍历过程中修改集合元素(尤其是修改集合长度)会抛出InvalidOperationException,同时你的原有实现还存在性能、安全隐患,下面是直接可用的优化方案:
方案1:使用for循环遍历索引(直接修改原集合,保留原索引)
直接遍历List的索引即可规避foreach的修改限制,同时匹配原有索引位置,不需要额外创建集合:
// 提前开启连接复用,避免重复创建连接 for (int i = 0; i < friendsOnline.Count; i++) { string currentEmail = friendsOnline[i]; // 使用参数化查询避免SQL注入风险 string query = "SELECT `username` FROM `accounts` WHERE email = @email"; using (var cmd = new MySqlCommand(query, conn)) { cmd.Parameters.AddWithValue("@email", currentEmail); using (var rdr = cmd.ExecuteReader()) { if (rdr.Read()) { // 直接按索引修改原集合元素,完全匹配原有位置 friendsOnline[i] = rdr.GetString(0); } } } }
方案2:使用等长新集合存储结果(避免修改原集合遍历异常,索引完全对齐)
如果你需要保留原始的邮箱列表不被修改,可以初始化一个和原List长度完全相同的新集合,按索引赋值即可保证索引匹配,不会出现索引错位问题:
// 初始化新列表,索引和原列表完全一一对应 List<string> friendsUsername = new List<string>(friendsOnline.Count); for (int i = 0; i < friendsOnline.Count; i++) { string currentEmail = friendsOnline[i]; string query = "SELECT `username` FROM `accounts` WHERE email = @email"; using (var cmd = new MySqlCommand(query, conn)) { cmd.Parameters.AddWithValue("@email", currentEmail); using (var rdr = cmd.ExecuteReader()) { // 查询不到用户名时保留原邮箱,避免空值 friendsUsername.Add(rdr.Read() ? rdr.GetString(0) : currentEmail); } } } // 最终friendsUsername的索引和原friendsOnline完全对应
高阶优化:批量查询降低数据库IO
如果在线好友数量较多,循环单次查询数据库效率很低,可以改用IN条件批量查询所有结果,再映射回原索引:
// 拼接参数化的IN条件,一次性查询所有邮箱对应的用户名 var paramList = new List<MySqlParameter>(); string inClause = string.Join(",", friendsOnline.Select((email, idx) => { string paramName = $"@email{idx}"; paramList.Add(new MySqlParameter(paramName, email)); return paramName; })); string query = $"SELECT `email`, `username` FROM `accounts` WHERE email IN ({inClause})"; // 建立邮箱到用户名的映射字典 var emailToUsername = new Dictionary<string, string>(); using (var cmd = new MySqlCommand(query, conn)) { cmd.Parameters.AddRange(paramList.ToArray()); using (var rdr = cmd.ExecuteReader()) { while (rdr.Read()) { emailToUsername[rdr.GetString("email")] = rdr.GetString("username"); } } } // 按索引更新原集合,只做一次内存遍历,性能提升明显 for (int i = 0; i < friendsOnline.Count; i++) { if (emailToUsername.TryGetValue(friendsOnline[i], out string username)) { friendsOnline[i] = username; } }
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

