You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用C#优化Excel逐行循环?6000行600列场景提速方案

优化Excel重复ID行差异高亮的代码方案

面对6000行×600列的大型工作表,逐行循环对比并单独设置单元格格式确实会因为Excel Interop的频繁交互导致严重的性能瓶颈。下面是针对性的优化思路和可直接复用的代码实现:

核心优化思路

  • 批量读取数据到内存:避免频繁访问Excel单元格,一次性将所有数据读入二维数组,内存中操作速度远快于直接操作Excel对象
  • 按ID分组处理:用字典将相同ID的行归类,彻底避免重复对比的冗余操作
  • 批量收集需要高亮的单元格:先找出所有差异单元格,再一次性应用格式,减少Excel的UI刷新次数
  • 临时禁用Excel的耗时功能:处理期间关闭屏幕更新、自动计算和事件触发,大幅提升运行效率

优化后的代码示例

private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    // 假设你已获取目标Worksheet对象,替换为你的实际对象
    var targetSheet = (Microsoft.Office.Interop.Excel.Worksheet)yourWorkbook.ActiveSheet;
    int totalRows = targetSheet.UsedRange.Rows.Count;
    int totalCols = targetSheet.UsedRange.Columns.Count;
    int idColumn = 1; // 假设ID在第1列,根据实际情况调整索引

    // 1. 一次性读取所有数据到内存数组(Excel对象的1-based索引)
    var dataRange = targetSheet.Range[targetSheet.Cells[2, 1], targetSheet.Cells[totalRows, totalCols]];
    object[,] dataArray = dataRange.Value as object[,];

    // 2. 按ID分组,键为ID值,值为对应Excel行号集合
    Dictionary<object, List<int>> idRowGroups = new Dictionary<object, List<int>>();
    for (int idx = 0; idx < dataArray.GetLength(0); idx++)
    {
        object idValue = dataArray[idx + 1, idColumn];
        int excelRow = idx + 2; // 对应Excel的实际行号(从第2行开始)
        
        if (!idRowGroups.ContainsKey(idValue))
        {
            idRowGroups[idValue] = new List<int>();
        }
        idRowGroups[idValue].Add(excelRow);

        // 更新进度条
        int progress = (idx + 1) * 100 / dataArray.GetLength(0);
        worker.ReportProgress(progress);
    }

    // 3. 临时禁用Excel的耗时功能
    targetSheet.Application.ScreenUpdating = false;
    targetSheet.Application.Calculation = Microsoft.Office.Interop.Excel.XlCalculation.xlCalculationManual;
    targetSheet.Application.EnableEvents = false;

    try
    {
        // 4. 处理每个ID组,找出差异列并高亮
        foreach (var group in idRowGroups)
        {
            var rowsInGroup = group.Value;
            if (rowsInGroup.Count <= 1)
                continue; // 只有一行的ID无需处理

            // 遍历每一列,检查组内是否存在差异
            for (int col = 1; col <= totalCols; col++)
            {
                // 取组内第一行的值作为基准
                object baseValue = dataArray[rowsInGroup[0] - 2 + 1, col]; // 转换为数组索引
                bool hasDifference = false;

                // 在内存数组中对比组内其他行的该列值
                foreach (int row in rowsInGroup.Skip(1))
                {
                    object currentValue = dataArray[row - 2 + 1, col];
                    if (!Equals(baseValue, currentValue))
                    {
                        hasDifference = true;
                        break;
                    }
                }

                // 若存在差异,批量高亮组内所有行的该列
                if (hasDifference)
                {
                    string rangeAddress = $"{targetSheet.Cells[rowsInGroup[0], col].Address}:{targetSheet.Cells[rowsInGroup.Last(), col].Address}";
                    targetSheet.Range[rangeAddress].Interior.Color = System.Drawing.Color.Yellow; // 可自定义高亮颜色
                }
            }

            // 按组更新进度(可选)
            int groupProgress = (int)((double)idRowGroups.Keys.ToList().IndexOf(group.Key) / idRowGroups.Count * 100);
            worker.ReportProgress(groupProgress);
        }
    }
    finally
    {
        // 5. 恢复Excel默认设置,避免影响后续操作
        targetSheet.Application.ScreenUpdating = true;
        targetSheet.Application.Calculation = Microsoft.Office.Interop.Excel.XlCalculation.xlCalculationAutomatic;
        targetSheet.Application.EnableEvents = true;
    }
}

额外优化建议

  • 若希望进一步提升性能,可以尝试使用EPPlus等第三方库替代Excel Interop,这类库直接操作文件而非Excel应用程序,速度会更快
  • 如果需要对比的列数量固定,可以提前过滤掉不需要检查的列,减少循环次数

内容的提问来源于stack exchange,提问作者Firdavs Kurbonov

火山引擎 最新活动