C#遍历数据并且删除数据报错的原因和解决方法

文章讨论了在使用foreach遍历集合时删除元素导致的“集合已修改;可能无法执行枚举操作”异常,以及使用for循环和多线程操作时遇到的问题。解决方案包括使用GetRange获取子集后再删除,以及在多线程环境下对集合操作进行同步锁定。

一:使用foreach遍历

在项目使用foreach遍历数据并且在遍历的过程中移除数据,会报错。

集合已修改;可能无法执行枚举操作的异常

原因是在使用foreach进行遍历时,本质上调用的是IEnumerator的MoveNext方法,是每次调用next()方法,都会检查expectedModCount和 ModCount值是否相等,当我们删除元素后,ModCount会改变与expectedModCount值不同,引起报错。

**

二:使用for循环

**
使用for循环遍历:注意这种方法删除集合元素会导致索引前移导致遍历问题。

ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.

原因是在RemoveAt方法中,当移除完元素之后,数组大小发生了变更,并且会将新的元素列表重新进行位置上的对齐。

使用getRange()方法获取数据,在使用移除方法。

 try
            {
                Console.WriteLine("定时器开始计时");
                timer1.Enabled = false;
                timer1.Stop();
                if (m_list.Count >= 15)
                {
                    List<string> my_temp_list = m_list.GetRange(0, 15);
                    foreach (var item in my_temp_list)
                    {
                        m_list.Remove(item);
                    }
                    foreach (var item in my_temp_list)
                    {
                        Thread.Sleep(100);
                    }
                    Console.WriteLine("本次处理的数据是:"+string.Join(",", my_temp_list));
                    Console.WriteLine("本次剩余的数据是:" + string.Join(",", m_list));
                }
                else
                {
                    List<string> my_temp_list = m_list.GetRange(0, m_list.Count);
                    foreach (var item in my_temp_list)
                    {
                        m_list.Remove(item);
                    }
                    foreach (var item in my_temp_list)
                    {
                        Thread.Sleep(100);
                    }
                    Console.WriteLine("本次处理的数据是:" + string.Join(",", my_temp_list));
                    Console.WriteLine("本次剩余的数据是:" + string.Join(",", m_list));
                }
                if (m_list.Count>0)
                {
                    timer1.Enabled = true;
                    timer1.Start();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

**

三:多线程操作数据:

**
使用迭代器的iterator.remove()在单线程下是不会报错的,但是在多线程情况下,一个线程修改了集合的modCount导致另外一个线程迭代时modCount与该迭代器的expectedModCount不相等,这也会报异常。

public class RemoveListForThreads implements Runnable {
    static List<String> alist = new ArrayList<>();
 
    public static void main(String[] args) {
        RemoveListForThreads s = new RemoveListForThreads();
        alist.add("a");
        alist.add("b");
        alist.add("c");
        alist.add("d");
 
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 1,
                TimeUnit.SECONDS, workQueue);
 
        for (int i = 0; i < 5; i++) {
            executor.execute(s);
        }
        executor.shutdown();
    }
 
 
    @Override
    public synchronized void run() {
        Iterator<String> iterator = alist.iterator();
            while (iterator.hasNext()) {
                String s = iterator.next();
                if (s.equals("c") && Thread.currentThread().getName().equals("pool-1-thread-1")) {
                    iterator.remove();
                    System.out.println(Thread.currentThread().getName() + " " + s);
                }
                System.out.println(Thread.currentThread().getName() + " " + s);
            }
            System.out.println(alist);
        }
}

上面这段代码创建了一个线程池,开启了五个线程,在run方法中遍历并删除“b”元素,如果该方法不加同步锁sychronized,也会抛出异常

<think> 我们遇到的问题是在C#中使用EPPlus 4.5.3时出现“worksheet position out of range”错误。这个错误通常表示我们尝试访问的工作表索引超出了工作簿中实际存在的工作表范围。 在EPPlus中,工作表的索引是从0开始的,并且索引值必须在0到(工作表总数-1)的范围内。如果尝试使用一个小于0或者大于等于工作表总数的索引,就会抛出这个异常。 解决方案如下: ### 1. **检查工作表的索引范围** - 获取工作簿中工作表的总数:`workbook.Worksheets.Count` - 确保要访问的工作表索引满足:$0 \leq \text{index} < \text{workbook.Worksheets.Count}$ ### 2. **使用工作表名称代替索引(推荐)** - 通过工作表名称访问工作表可以避免索引越界的问题。 - 示例代码: ```csharp using (var package = new ExcelPackage(new FileInfo("yourfile.xlsx"))) { var workbook = package.Workbook; // 通过名称访问 if (workbook.Worksheets.Any(sheet => sheet.Name == "Sheet1")) { var worksheet = workbook.Worksheets["Sheet1"]; } else { // 处理工作表不存在的情况 } } ``` ### 3. **历工作表名称列表** - 在不确定索引的情况下,可以先获取所有工作表的名称,然后根据名称访问。 ```csharp var sheetNames = workbook.Worksheets.Select(sheet => sheet.Name).ToList(); // 打印所有工作表名称 foreach (var name in sheetNames) { Console.WriteLine(name); } ``` ### 4. **插入新工作表时的注意事项** - 当使用`Worksheets.Add`方法插入新工作表时,可以指定插入位置,但要注意位置索引不能超过当前工作表总数(即索引从0到Count)。 - 例如,当前有3个工作表,则插入位置可以是0、1、2、3(在最后插入)。如果指定位置大于3,则会抛出异常。 - 正确示例: ```csharp // 在索引为2的工作表之前插入一个新工作表(即插入后新工作表索引为2) var newSheet = workbook.Worksheets.Add("NewSheet", 2); ``` ### 5. **处理动态索引的场景** - 当索引是动态计算得到时,务必进行范围检查: ```csharp int targetIndex = ...; // 动态计算的索引 if (targetIndex >= 0 && targetIndex < workbook.Worksheets.Count) { var worksheet = workbook.Worksheets[targetIndex]; } else { // 处理索引超出范围的情况 } ``` ### 6. **完整代码示例** ```csharp using (var package = new ExcelPackage(new FileInfo("example.xlsx"))) { var workbook = package.Workbook; // 情况1:通过索引访问(确保索引有效) if (workbook.Worksheets.Count > 0) { // 访问第一个工作表(索引0) var firstSheet = workbook.Worksheets[0]; } // 情况2:通过名称访问 string sheetName = "DataSheet"; if (workbook.Worksheets.Any(ws => ws.Name == sheetName)) { var dataSheet = workbook.Worksheets[sheetName]; } else { Console.WriteLine($"工作表 '{sheetName}' 不存在!"); } // 情况3:插入新工作表 // 在最后插入 var newSheet1 = workbook.Worksheets.Add("NewSheet1"); // 在索引0的位置插入(即成为第一个工作表) var newSheet2 = workbook.Worksheets.Add("NewSheet2", 0); } ``` > **关键建议**:优先使用工作表名称进行访问,可以避免大多数索引越界问题。在必须使用索引的情况下,务必检查索引的有效性[^1]。 ### 常见错误场景 - **错误场景1**:认为索引从1开始(注意:EPPlus中索引是0-based)。 - **错误场景2**:在循环中使用了固定索引,但循环次数超过了工作表总数。 - **错误场景3**:在工作表被删除后,仍然使用旧的索引访问。 §§相关问题§§ 1. 如何在EPPlus中复制一个工作表? 2. 如何用EPPlus删除Excel中的工作表? 3. EPPlus支持哪些Excel文件格式? 4. EPPlus中如何隐藏/显示工作表? [^1]: 基于EPPlus官方文档关于Worksheets集合的说明。 </think> ### C# EPPlus 4.5.3 "Worksheet Position Out of Range" 错误解决方案 在 EPPlus 4.5.3 中,"Worksheet Position Out of Range" 错误通常表示尝试访问不存在的索引位置。以下是完整解决方案: #### 1. **验证工作表索引范围** - 工作表索引从 0 开始 - 有效范围:$0 \leq \text{index} < \text{Worksheets.Count}$ - 访问前检查索引有效性: ```csharp int targetIndex = 2; // 目标索引 if (targetIndex >= 0 && targetIndex < package.Workbook.Worksheets.Count) { ExcelWorksheet sheet = package.Workbook.Worksheets[targetIndex]; } else { Console.WriteLine($"错误:索引 {targetIndex} 超出范围,最大索引为 {package.Workbook.Worksheets.Count - 1}"); } ``` #### 2. **使用工作表名称替代索引(推荐)** ```csharp string sheetName = "DataSheet"; if (package.Workbook.Worksheets.Any(ws => ws.Name == sheetName)) { ExcelWorksheet sheet = package.Workbook.Worksheets[sheetName]; } else { Console.WriteLine($"错误:工作表 '{sheetName}' 不存在"); } ``` #### 3. **安全获取所有工作表信息** ```csharp // 获取所有工作表名称索引 var sheetInfo = package.Workbook.Worksheets .Select((ws, index) => new { Index = index, Name = ws.Name }) .ToList(); Console.WriteLine("可用工作表:"); foreach (var info in sheetInfo) { Console.WriteLine($"[{info.Index}] {info.Name}"); } ``` #### 4. **创建工作表时的防错处理** 当使用 `Worksheets.Add()` 方法时: ```csharp // 安全添加工作表 string newSheetName = "Report"; if (!package.Workbook.Worksheets.Any(ws => ws.Name == newSheetName)) { // 在索引1的位置插入新工作表 ExcelWorksheet newSheet = package.Workbook.Worksheets.Add(newSheetName, 1); } ``` **注意**:插入位置必须满足 $0 \leq \text{position} \leq \text{Worksheets.Count}$ #### 5. **常见错误场景修复** - **错误示例**: ```csharp // 错误:假设索引从1开始 ExcelWorksheet sheet = package.Workbook.Worksheets[1]; ``` - **修正方案**: ```csharp // 正确:使用0-based索引或名称 ExcelWorksheet firstSheet = package.Workbook.Worksheets[0]; ExcelWorksheet dataSheet = package.Workbook.Worksheets["Data"]; ``` #### 6. **完整代码示例** ```csharp using (var package = new ExcelPackage(new FileInfo("data.xlsx"))) { try { // 方法1:安全索引访问 if (package.Workbook.Worksheets.Count > 0) { int targetIndex = 0; if (targetIndex < package.Workbook.Worksheets.Count) { ExcelWorksheet sheet = package.Workbook.Worksheets[targetIndex]; Console.WriteLine($"访问工作表: {sheet.Name}"); } } // 方法2:名称访问(最佳实践) string targetSheet = "SalesData"; var worksheet = package.Workbook.Worksheets .FirstOrDefault(ws => ws.Name.Equals(targetSheet, StringComparison.OrdinalIgnoreCase)); if (worksheet != null) { // 处理工作表数据 } else { Console.WriteLine($"创建新工作表: {targetSheet}"); package.Workbook.Worksheets.Add(targetSheet); } package.Save(); } catch (Exception ex) { Console.WriteLine($"操作失败: {ex.Message}"); } } ``` > **关键提示**:EPPlus 的工作表索引始终从 0 开始,最大有效索引为 `Worksheets.Count - 1`。推荐使用工作表名称进行访问以避免索引错误[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值