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

.net 在允许我实例化工作表的单元格时,使用什么体系结构来解决SystemOutOfMemoryException?,.net,vb.net,performance,excel,interop,.net,Vb.net,Performance,Excel,Interop,摘要 这个问题是对一个愿望的后续,即设计一个简单的电子表格API,同时使它对熟悉Excel的人保持用户友好 总之,这个问题与以下两个问题有关: 1. ; 2. . 目标 提供一个简化的Excel API,用作Nevragic组件(如应用程序、工作簿、工作表和范围类/接口)的包装器,同时仅公开这些组件中最常用的对象属性 用法示例 这个使用示例的灵感来自单元测试,单元测试使我能够将这个解决方案提升到现在的水平 Dim file as String = "C:\Temp\WriteTest.xls"

摘要

这个问题是对一个愿望的后续,即设计一个简单的电子表格API,同时使它对熟悉Excel的人保持用户友好

总之,这个问题与以下两个问题有关:
1. ;
2. .

目标

提供一个简化的Excel API,用作Nevragic组件(如
应用程序
工作簿
工作表
范围
类/接口)的包装器,同时仅公开这些组件中最常用的对象属性

用法示例

这个使用示例的灵感来自单元测试,单元测试使我能够将这个解决方案提升到现在的水平

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
现在我们打开它:

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
讨论

实例化Excel工作簿时:

  • 工作表的实例在工作簿中自动初始化。工作表集合
  • 初始化时,工作表通过可以表示一个或多个单元格的
    范围
    对象初始化其单元格
  • 一旦工作表存在,就可以立即访问这些单元格及其所有属性

    我的愿望是重现这种行为,以便

  • 工作簿类构造函数使用本机工作表初始化工作簿.Sheets集合属性
  • 工作表类构造函数使用本机单元格初始化工作表.Cells集合属性
  • 我的问题来自于初始化工作表.Cells集合属性时的工作表类构造函数,如#2所示

    思想

    根据上述问题,我希望找出另一种架构,使我能够:

  • 在需要时访问单元格的特定功能
    范围
  • 通过my
    ICell
    界面交付最常用的属性
  • 从工作表的初始化开始,可以访问工作表的所有
    区域
    单元格
  • 请记住,访问
    Range.Value
    属性是使用互操作与底层Excel应用程序实例进行交互的最快方式

    因此,我想用单元格的名称初始化我的
    readonlydictionary(of String,ICell)
    ,而不立即包装
    Range
    接口的实例,这样我就可以简单地生成行和列索引以及单元格的名称来索引我的字典,然后,仅当需要访问或格式化特定单元格或单元格区域时,才分配
    Cell.NativeCell
    属性

    这样,字典中的数据将使用从
    工作表
    类构造函数中生成的列索引中获得的单元格名称进行索引。那么,当我们这样做的时候:

    Using mgr As ISpreadsheetManager = New SpreadsheetManager()
        Dim wb As IWorkbook = mgr.CreateWorkbook()
        wb.Sheet(1).Cells("A1").Value = 3.1415926 // #1:
    End Using
    
    #1:这将允许我使用
    单元格
    类中的索引将给定值写入特定单元格,这比直接针对
    范围使用其名称更快

    问题和顾虑

    此外,当使用
    UsedRange.get_Value()
    单元格.get_Value()
    时,将返回对象(,)数组

    1。因此,我是否应该只满足于使用单元格的
    对象(,)
    数组,而不必以某种方式格式化它?

    2。如何构建这些工作表和单元格类,以便在使用
    对象(,)
    数组时获得最佳性能,同时保持单元格实例可能表示或包装单个单元格范围的可能性?

    感谢你们中任何一位花时间阅读我的文章的人,我最真诚的感谢那些回答我的人


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

    基于这些假设:

  • 假设Excel工作表有256列和65536行

  • 假设一次需要实例化16777216(256*65536)个单元

  • 考虑到工作表最常见的使用少于1000行,少于100列

  • 考虑到我需要它能够引用带有地址的单元格(“A1”);和

  • 根据基准测试,一次访问所有值并将其加载到内存中的
    对象[,]
    ,是处理底层Excel工作表的最快方法*

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

    最后,设计模式来营救!=)

    公共课程表:ISHET{
    公共工作表(Microsoft.Office.Interop.Excel.Worksheet nativeSheet){
    NativeSheet=NativeSheet;
    单元格=新的单元格集合(此);
    }
    public Microsoft.Office.Interop.Excel.Worksheet NativeSheet{get;private set;}
    公共单元格集合单元格{get;private set;}
    }
    公开密封类别收集{
    私有索引单元;
    私人只读字典;
    公共牢房收藏(ISHET表){
    _单元格=新字典();
    _readonlyCells=新的ReadonlyDictionary(_单元格);
    板材=板材;
    
    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; }
    }