【VB.NET】ClosedXML:パフォーマンスと効率の向上

MEMO
  • この記事は GitHub - ClosedXML - Wiki を参考に書かせていただいたものですが、C# から VB.NET への書き換えをし、英語から日本語へ翻訳しただけでなく、元記事の構成・コード・コメント等を変更している場合もありますのでご了承ください。
  • 現在 ClosedXML のメンテナンスをご担当されている Francois Botha 様からの掲載許可もいただいてます。
  • この記事のコードサンプルについては、Imports ClosedXML.Excel が宣言されていることを前提としています。
  • ClosedXML の作成者について:Francois Botha, Aleksei Pankratev, Manuel de Leon, Amir Ghezelbash
  • ClosedXML のライセンスについて:GitHub - ClosedXML - MIT License

範囲の自動調整をしないようにする

通常、ClosedXML は挿入・削除の処理を追跡し、関連する範囲を適切に調整します。以下はその例です。

Dim testRow = worksheet.Row(1)
worksheet.Row(1).InsertRowsAbove(1)
'ここで testRow が指しているのはワークシートの2行目(1行目ではない)

もしもこの調整が不要な場合は、調整を行わないようにできます。そうすることにより、メモリの節約とパフォーマンスの改善が期待できます。

この機能をオフにするには、ワークブックを開く際の引数として XLEventTracking.Disabled を指定します。

適切なラムダ式を使用する

.Where などにすべてのオブジェクトを渡すかわりに、Cells/Rows/Columns のメソッドに述部を与えることを検討してください。

 

改善の余地があるやり方

For Each row in worksheet.RowsUsed.Where(Function(r) r.FirstCell.GetString = "A")
  'row に対する処理
Next

 

適切なやり方

Dim rows = worksheet.RowsUsed(Function(r) r.FirstCell.GetString = "A")
For Each row in rows
    'row に対する処理
Next
MEMO by エレン・イースト

元記事では rows に対して Using を使うように書かれているが、rows (IXLRows) には System.IDisposable が実装されていないので Using を使うことはできない。

 

改善の余地があるやり方

Dim column = range.Columns.First(Function(c) c.FirstCell.GetString = "A")

 

適切なやり方

Dim column = range.FirstColumnUsed(Function(c) c.FirstCell.GetString = "A")

Using キーワードの使用

MEMO

ワークブックのイベント追跡をオフにしている場合、Using キーワードは不要です。詳細については「範囲の自動調整をしないようにする」を参照してください。

ループ内で範囲を表すオブジェクトを処理する場合は Using キーワードを使用してください。これにはワークブック、ワークシート、行(row, rows)、列(column, columns)が含まれます。

下記のサンプルコードで処理しているのは大きなファイルだと考えてください。

 

改善の余地があるやり方

Dim wb = New XLWorkbook
Dim ws = wb.Worksheets.Add("Sheet1")
For ro As Integer = 1 To 1000000
    ws.Row(ro).FirstCell.Value = ro
Next

 

適切なやり方

Using wb = New XLWorkbook
    'worksheet は workbook によって破棄されるため、
    'worksheet に対して Using を使用する必要はありません。
    'worksheet はループの中で作成されているのではなく、
    'worksheet の使用後すぐに workbook が破棄されます。
    Dim ws = wb.Worksheets.Add("Sheet1")
    For ro As Integer = 1 To 1000000
        Dim row = ws.Row(ro)
        row.FirstCell().Value = ro
    Next
End Using
MEMO by エレン・イースト

元記事では row に対しても Using を使うように書かれているが、row (IXLRow) には System.IDisposable が実装されていないので Using を使うことはできない。

 

改善の余地があるやり方

Dim wb = new XLWorkbook
For wsNum As Integer = 1 To 10
    Dim ws = wb.Worksheets.Add("Sheet" & wsNum)
    'worksheet の処理
Next

 

適切なやり方

Using wb = New XLWorkbook
    For wsNum As Integer = 1 To 10
    Dim ws = wb.Worksheets.Add("Sheet" & wsNum)
        'worksheet の処理
    Next
End Using
MEMO by エレン・イースト

元記事では ws に対しても Using を使うように書かれているが、ws (IXLWorksheet) には System.IDisposable が実装されていないので Using を使うことはできない。

パフォーマンスの改善に役立つその他のこと

  • セルを取得するときは文字列ではなく数値を使用するようにしてください。たとえば Cell("A1") とするのではなく Cell(1, 1) と指定します。小さなシートではこの違いをほとんど感じられませんが、何万ものセルを処理する場合には無視できないものになります。
  • ループの中で行や列の挿入・削除をしないでください。それよりも挿入する行や列の数を計算してから一度に処理したほうがずっと高速です。なぜなら、挿入や削除を行うたびに、ライブラリがすべての式を再計算することになるからです。

範囲の取得に便利なメソッド

最初・最後の行や列の取得は、頻繁に必要となる処理です。毎回めんどうなコードを書かなくても下記のメソッドが使用できます。

range.FirstCell
range.FirstCellUsed
range.FirstColumn
range.FirstColumnUsed
range.FirstRow
range.FirstRowUsed

range.LastCell
range.LastCellUsed
range.LastColumn
range.LastColumnUsed
range.LastRow
range.LastRowUsed

 

使用済みの範囲を計算する必要もありません。次のコードを利用してください。

worksheet.RangeUsed

 

「【VB.NET】ClosedXML の使い方」の一覧を表示

購読する
通知を受け取る対象
guest
0 Comments
Inline Feedbacks
View all comments