在 EPPlus 中枚举范围时,是否总是按以下顺序检索单元格:先按行排序,然后按列排序?

When enumerating a range in EPPlus, are cells always retrieved in the following order : sorted by row, then by column?

假设我有以下代码:

using (ExcelPackage package = new ExcelPackage(...))
{
   foreach(var cell in package.Workbook.Worksheets[1].Cells["A1:E5"])
   {
       //do something with "cell"
   }
}

和以下 Excel 工作表:

   A      B      C      D      E
1  foo    bar    .      .      .
2  .      .      .      .      .
3  .      .      hello  .      .      
4  .      .      world  .      .
5  .      .      .      .      .

检索元素的顺序是否始终为:

foo => bar => hello => world ?

从我目前的尝试来看,这似乎总是正确的。 但这并不意味着总是如此。

似乎 Excel 在 .xlsx 文件中也按该顺序序列化单元格,即使单元格已按不同顺序编辑。

ExcelWorksheet class 中的文档并没有说太多:

//
// Summary:
//     Provides access to a range of cells
public ExcelRange Cells { get; }

我回答我自己的问题。

TLTR:在当前的 EPPlus 版本 (v4.5.1) 中,给定范围的单元格始终按以下顺序枚举:先按行排序,然后按列排序。


我通过查看 EPPlus 发现了这一点 implementation

给定工作表的单元格存储在 RangeCollection 对象中:

class ExcelWorksheet
{
    RangeCollection _cells;
}

RangeCollection 包含一个单元格列表。此列表始终按 RangeID 排序。 RangeID 是行/列/工作表索引的组合。 这允许 EPPlus 通过执行二进制搜索快速找到单元格的索引(对于给定的行和列)。

class RangeCollection
{
    List<IRangeID> _cells;

    int IndexOf(ulong rangeID)
    {
        return Array.BinarySearch(...);
    }
}

class ExcelCell : IRangeID
{
    ulong RangeID
    {
        get
        {
            return GetCellID(_worksheet.SheetID, Row, Column);
        }
    }

    ulong GetCellID(int SheetID, int row, int col)
    {
        return ((ulong)SheetID) + (((ulong)col) << 15) + (((ulong)row) << 29);
    }
}

在枚举给定范围内的单元格时,EPPlus 将使用该排序列表来枚举范围内的单元格:

class ExcelRange 
{
    public bool MoveNext()
    {
        _index++;
        //...
        if (...) 
        {
           GetStartIndexEnum(_fromRow, _fromCol, _toRow, _toCol);
           //...
           GetNextIndexEnum(_fromRow, _fromCol, _toRow, _toCol);
        }
    }

    object IEnumerator.Current
    {
        get
        {
            return /*...*/ _worksheet._cells[_index] as ExcelCell /*...*/
        }
    }
}

GetStartIndexEnum()GetNextIndexEnum()用于快速跳过当前枚举范围之外的单元格。按照与 RangeCollection 本身相同的顺序检查和枚举单元格,后者始终排序。