在允许我实例化工作表的单元格的同时,使用什么体系结构来解决此SystemOutOfMemoryException?

Wil*_*ler 7 .net vb.net excel performance interop

摘要

这个问题是建立一个简单的电子表格API的愿望的后续,同时保持对熟悉Excel的用户友好.

总结一下,这个问题与以下两个问题有关:
1.如何从索引实现列自命名?;
2. 如何使此自定义工作表初始化更快?.

目的

为了提供用作包装在nevralgic组件,如一个简化的Excel API Application,所述WorkbookWorksheetRange同时暴露只为每个这些最常用的对象属性的类/接口.

用法示例

这个用法示例的灵感来自单元测试,使我能够将此解决方案带到现在的位置.

Dim file as String = "C:\Temp\WriteTest.xls"

Using mgr As ISpreadsheetManager = New SpreadsheetManager()
    Dim wb as IWorkbook = mgr.CreateWorkbook()
    wb.Sheets("Sheet1").Cells("A1").Value = 3.1415926
    wb.SaveAs(file)
End Using
Run Code Online (Sandbox Code Playgroud)

现在我们打开它:

Dim file as String = "C:\Temp\WriteTest.xls"

Using mgr As ISpreadsheetManager = New SpreadsheetManager()
    Dim wb as IWorkbook = mgr.OpenWorkbook(file)
    // Working with workbook here...
End Using
Run Code Online (Sandbox Code Playgroud)

讨论

在实例化Excel工作簿时:

  1. Workbook实例在Workbook.Sheets集合中自动初始化;
  2. 初始化时,工作表通过Range可以表示一个或多个单元格的对象初始化其单元格.

只要工作表存在,就可以立即访问这些单元格及其所有属性.

我的愿望是重现这种行为

  1. Workbook类构造函数使用本机工作表初始化Workbook.Sheets集合属性;
  2. Worksheet类构造函数使用本机单元格初始化Worksheet.Cells集合属性.

我的问题来自Worksheet类构造函数,同时初始化#2中说明的Worksheet.Cells集合属性.

思考

在上述相关问题遇到问题后,我想找出另一种架构,让我能够:

  1. Range在需要时访问单元的特定功能;
  2. 通过我的ICell界面提供最常用的属性;
  3. 可以Range从初始化访问工作表的所有单元格.

同时请记住,Range.Value使用Interop 访问属性是与底层Excel应用程序实例可能的最快交互.

所以,我想初始化我的ReadonlyOnlyDictionary(Of String, ICell)与细胞的名称,而不是立即包裹的实例Range接口,这样我就简单地生成随着细胞的姓名索引我的字典里,然后行和列的索引,分配Cell.NativeCell,只有当一个属性想要访问或格式化特定的细胞或细胞范围.

这样,字典中的数据将使用从Worksheet类构造函数中生成的列索引获取的单元格的名称进行索引.然后,当一个人这样做:

Using mgr As ISpreadsheetManager = New SpreadsheetManager()
    Dim wb As IWorkbook = mgr.CreateWorkbook()
    wb.Sheet(1).Cells("A1").Value = 3.1415926 // #1:
End Using
Run Code Online (Sandbox Code Playgroud)

#1:这将允许我使用我的Cell类中的索引将给定值写入特定单元格,这比直接使用其名称更快Range.

问题和疑虑

此外,使用UsedRange.get_Value()或时Cells.get_Value(),它返回Object(,)数组.

那么我是否应该对使用Object(,)数组的数组感到满意,而不能以某种方式格式化它?

2.如何构建这些Worksheet和Cell类,以便在使用Object(,)数组时提供最佳性能,同时保持Cell实例可能表示或包装单个单元格Range的可能性?

感谢任何一个花时间阅读我的帖子的人,也感谢那些回答的人.

Wil*_*ler 0

使用的体系结构已经经历了我命名为的对象类CellCollection。它的作用如下:

基于这些假设:

  1. 假设 Excel 工作表有 256 列和 65536 行;

  2. 假设一次需要实例化 16,777,216 (256 * 65536) 个单元;

  3. 鉴于工作表最常见的用途是少于 1,000 行和少于 100 列;

  4. 鉴于我需要它能够引用单元格及其地址(“A1”);和

  5. 鉴于一次性访问所有值并将其加载到object[,]内存中的基准测试是使用基础 Excel 工作表的最快方法,*

我考虑过不实例化任何单元格,让界面CellCollection中的属性IWorksheet在实例化时初始化并清空,现有工作簿除外。因此,当打开工作簿时,我验证其NativeSheet.UsedRange为空或返回 null(在 Visual Basic 中为 Nothing),否则,我已经在内存中获取了使用的“本机单元格”,因此只需在索引它们时将它们添加到我的内部CellCollection字典中以及他们各自的地址。

最后,延迟初始化设计模式来救援!=)

public class Sheet : ISheet {
    public Worksheet(Microsoft.Office.Interop.Excel.Worksheet nativeSheet) {
        NativeSheet = nativeSheet;
        Cells = new CellCollection(this);
    }

    public Microsoft.Office.Interop.Excel.Worksheet NativeSheet { get; private set; }

    public CellCollection Cells { get; private set; }
}

public sealed class CellCollection {
    private IDictionary<string, ICell> _cells;
    private ReadOnlyDictionary<string, ICell> _readonlyCells;

    public CellCollection(ISheet sheet) {
        _cells = new Dictionary<string, ICell>();
        _readonlyCells = new ReadonlyDictionary<string, ICell>(_cells);
        Sheet = sheet;
    }

    public readonly ReadOnlyDictionary<string, ICell> Cells(string addresses) {
        get {
            if (string.IsNullOrEmpty(addresses) || 0 = address.Trim().Length)
                throw new ArgumentNullException("addresses");

            if (!Regex.IsMatch(addresses, "(([A-Za-z]{1,2,3}[0-9]*)[:,]*)"))
                throw new FormatException("addresses");

            foreach(string address in addresses.Split(",") {
                Microsoft.Office.Interop.Excel.Range range = Sheet.NativeSheet.Range(address)

                foreach(Microsoft.Office.Interop.Excel.Range cell in range) {
                    ICell c = null;
                    if (!_cells.TryGetValue(cell.Address(false, false), c)) { 
                        c = new Cell(cell);
                        _cells.Add(c.Name, c);
                    }
                }
            }

            return _readonlyCells;
        }
    }

    public readonly ISheet Sheet { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

显然,这是第一次尝试,到目前为止效果还不错,性能超出了可接受的范围。谦虚地说,我觉得它可以使用一些优化,尽管我现在会这样使用它,并在需要时稍后进行优化。

写完这个集合后,我能够达到预期的行为。现在,我将尝试实现一些 .NET 接口,使其可用于某些IEnumerableIEnumerable<T>ICollectionICollection<T>等,以便它可以分别被视为真正的 .NET 集合。

请随意评论并对此代码提出建设性的替代方案和/或更改,以便它可能变得比当前更强大。

我确实希望有一天这能达到目的。

谢谢阅读!=)