如何使这个自定义工作表初始化更快?

时间:2011-03-02 21:39:23

标签: .net performance excel office-interop

  

摘要

这个问题在某种程度上是对这个问题的后续行动:
How to implement column self-naming from its index?

测试了上述链接问题答案中提供的代码后,我终于遇到了严重的性能问题。

  

效果问题

在Sheet初始化时,即初始化Sheet的Cells时,会出现性能问题。

    ''' <summary>
    ''' Initialize an instance of the Company.Project.Sheet class.
    ''' </summary>
    ''' <param name="nativeSheet">The native worksheet from which to initialize.</param>
    Friend Sub New(ByVal nativeSheet As Microsoft.Office.Interop.Excel.Worksheet)
        _nativeSheet = nativeSheet
        Dim cells As IDictionary(Of String, ICell) = New Dictionary(Of String, ICell)()

        'These iterations hurt the performance of the API...'
        For rowIndex As Integer = 1 To _nativeSheet.Rows.Count Step 1
            For colIndex As Integer = 1 To _nativeSheet.Columns.Count Step 1
                Dim c As ICell = New Cell(_nativeSheet.Cells(rowIndex, colIndex))
                cellules.Add(c.Name, c)
            Next
        Next

        _cellules = New ReadOnlyDictionary(Of String, ICell)(cells)
    End Sub
  
      
  • ReadOnlyDictionary(Of TKey,TValue)
      A custom read-only dictionary that simply wraps a IDictionary(Of TKey, TValue) to prevent modifications.
  •   
     

讨论

我正在这样工作,因为底层电子表格工作表中的每个单元格都从工作表的初始化初始化到结束,即工作表被处理或完成时。因此,我希望初始化Sheet的单元格的方式相同,但我也希望保持在命名(“A1”)单元格上使用索引单元格的性能提升,同时保持API用户的易用性引用具有其名称的单元格,这就是我打算使用字典的方式,这样当我引用单元格“A1”时,我将此密钥访问到我的字典中并相应地寻址单元格(1,1)。

  • 除此之外,我知道使用Worksheet.UsedRange属性从工作表中读取更快的方法,该属性将所有使用过的单元格返回到2D矩阵中。

    如果对于我可以初始化我的Cell类的多个实例的单元格集,无论如何相同或大致相同,这将是伟大的,并且高效!

  • 我还想到在内存中用100 x 100矩阵单元初始化,同时用我的字典映射它们,因为很少使用整个单元格的单元格。因此,我仍然想到一种方法,我将不得不访问一个尚未初始化的单元格,让我们说Cell(120,120)。理想情况下,我认为,该程序必须初始化最初初始化的最大Cell(100,100)之间的所有单元格,直到Cell(120,120)。我在这里清楚了吗?随意要求澄清! =)

  • 另一种选择可能是我只将单元格的名称初始化为字典并在内存中保留行和列索引,而不是使用其nativeCell初始化Cell实例,比如说Range。这是我的Cell类的代码来说明我的意思。

    ''” '''表示工作表中的单元格。 “”” “”” 朋友类细胞     实现ICell

    Private _nativeCell As Microsoft.Office.Interop.Excel.Range
    Private _name As String
    
    ''' <summary>
    ''' Initializes a new instance of the Company.Project.Cell class.
    ''' </summary>
    ''' <param name="nativeCell">The Microsoft.Office.Interop.Excel.Range to wrap.</param>
    Friend Sub New(ByVal nativeCell As Microsoft.Office.Interop.Excel.Range)
        _nativeCell = nativeCell
    End Sub
    
    Public ReadOnly Property NativeCell() As Microsoft.Office.Interop.Excel.Range Implements ICellule.NativeCell
        Get
            Return _nativeCell 
        End Get
    End Property
    
    Public ReadOnly Property Column() As Integer Implements ICell.Column
        Get
            Return _nativeCell.Column
        End Get
    End Property
    
    Public ReadOnly Property Row() As Integer Implements ICell.Row
        Get
            Return _nativeCell.Row
        End Get
    End Property
    
    Public ReadOnly Property Name() As String Implements ICellule.Name
        Get
            If (String.IsNullOrEmpty(_name) OrElse _name.Trim().Length = 0) Then _
                _name = GetColumnName()
    
            Return _nom
        End Get
    End Property
    
    Public Property Value() As Object Implements ICellule.Value
        Get
            Return _nativeCell.Value2
        End Get
        Set(ByVal value As Object)
            _nativeCell.Value2 = value
        End Set
    End Property
    
    Public ReadOnly Property FormattedValue() As String Implements ICellule.FormattedValue
        Get
            Return _nativeCell.Text
        End Get
    End Property
    
    Public ReadOnly Property NumericValue() As Double? Implements ICellule.NumericValue
        Get
            Return Value
        End Get
    End Property
    
  

问题

  1. 我的其他选择是什么?

  2. 还有其他方法可以解决吗?

  3. 我是否有办法让实际方法与性能问题一致?

  4. 为了您的信息,这个问题在测试时超时,因此测试从未在可接受的时间范围内结束,实际上需要几个世纪......

    欢迎任何想法!我对其他解决方案或方法持开放态度,这有助于我在解决这一性能问题时实现这一目标。

    谢谢大家! =)

      

    编辑#1

    感谢Maxim Gueivandov,他的解决方案解决了我在这个问题中解决的问题。

    除此之外,此解决方案还存在另一个问题:SystemOutOfMemoryException,这将在另一个问题中解决。

    我的最诚挚感谢Maxim Gueivandov。

1 个答案:

答案 0 :(得分:1)

您可以尝试在一个跃点中获取所使用范围内的所有单元格,从而避免在每次迭代迭代时调用Cells(rowIndex, colIndex)(我猜Cells隐藏了一个互操作调用,可能有一个绩效影响)。

Dim usedRange As Range = nativeSheet.UsedRange
Dim cells(,) As Object = DirectCast(usedRange.get_Value( _
    XlRangeValueDataType.xlRangeValueDefault), Object(,))
[... do your row/col iterations ...]

您可以在以下文章中找到一些基于这些假设的性能提示: C# Excel Interop Use 。最值得注意的是,检查基准部分:

  

=== C#的Excel互操作基准===

     

细胞[]:30.0秒

     

get_Range(),Cells []:15.0秒

     

UsedRange,get_Value():1.5秒   [最快]