QA@IT
この質問・回答は、@ITの旧掲示板からインポートされたものです。

C#(VSTO) 配列経由でセルの値を高速で読み書きする方法

C#(VSTO) 配列経由でセルの値を高速で読み書きする方法

課題1
C#の二次元配列の値をセルに一括で書き込みたい。
専用メソッド(SetValue)を作成したがパフォーマンスをより向上させたい。
課題2
セルの値をC#の二次元配列に一括で読み込みたい。
専用メソッド(GetValue)を作成したがパフォーマンスを向上させたい。

環境
VisualStudioTools for OfficeSystem 経由でC#からExcel2003を制御
一括で読み書きする件数は数千件程度

疑問1
セルに書き込んでいる時だけExcelの計算方法を自動計算から手動計算に
切り替えることができれば確実にパフォーマンスが向上すると考えている。
C#からExcelの計算方法を変更する方法が分からない。
疑問2
現在は、セル1個ずつに対して値を読み書きしているからパフォーマンスが
芳しくないのではと思っている。
箱型の領域(Range)に対して一括で読み書きするもっと効率の良い方法があるか?
疑問3
タブと改行で区切られた巨大な文字列を生成して一括で貼り付けるの方法は
検討の価値があるか?

以下、読み書き専用のクラス

namespace Excel連携テスト
{
using System;
using Excel = Microsoft.Office.Interop.Excel;

public class Bridge
{
    public Bridge(Excel.Workbook workbook)
    {
        thisWorkbook = workbook;
    }

    public void GetValue(object[,] cellValue,string sheetName,int rowIndex,int columnIndex)
    {
        Excel.Worksheet worksheet1 = thisWorkbook.Worksheets.get_Item(sheetName) as Excel.Worksheet;

        int rowLength = cellValue.GetLength(0);
        int columnLength = cellValue.GetLength(1); 

        for(int i = 0;i < rowLength;i++)
        {
            for(int j = 0;j < columnLength;j++)
            {
                cellValue[i,j] = (worksheet1.Cells.get_Item(rowIndex+i,columnIndex+j) as Excel.Range).Value2;
            }
        }
    }

    public void SetValue(object[,] cellValue,string sheetName,int rowIndex,int columnIndex)
    {
        Excel.Worksheet worksheet1 = thisWorkbook.Worksheets.get_Item(sheetName) as Excel.Worksheet;

        int rowLength = cellValue.GetLength(0);
        int columnLength = cellValue.GetLength(1); 

        for(int i = 0;i < rowLength;i++)
        {
            for(int j = 0;j < columnLength;j++)
            {
                (worksheet1.Cells.get_Item(rowIndex+i,columnIndex+j) as Excel.Range).Value2 = cellValue[i,j];
            }
        }
    }

    private Excel.Workbook thisWorkbook;
}

}

以下、専用クラスの呼び出し例

    protected void ThisWorkbook_Open()
    {
        Bridge bridge1 = new Bridge(thisWorkbook);
        object[,] object1 = new object[1000,2];
        for(int i = 0;i < object1.GetLength(0);i++)
        {
            for(int j = 0;j < object1.GetLength(1);j++)
            {
                object1[i,j] = i+j*1000;
            }
        }
        DateTime time1 = DateTime.Now;
        bridge1.SetValue(object1,"Sheet1",1,1);
        object[,] object2 = new object[1000,2];
        bridge1.GetValue(object2,"Sheet1",1,1);
        bridge1.SetValue(object2,"Sheet1",1,5);
        DateTime time2 = DateTime.Now;
        TimeSpan timeSpan1 = time2.Subtract(time1);
        object[,]  answer1;
        answer1 = new object[1,1];
        answer1[0,0] = String.Format("経過={0:00}:{1:00}:{2:00}.{3:000}",timeSpan1.Hours,timeSpan1.Minutes,timeSpan1.Seconds,timeSpan1.Milliseconds);
        bridge1.SetValue(answer1,"Sheet1",1,10);

    }

質問者:ひろし

回答

画面更新の制御は
thisApplication.ScreenUpdating = false;
thisApplication.ScreenUpdating = true;
自動計算の制御は
thisApplication.Calculation = Excel.XlCalculation.xlCalculationManual;
thisApplication.Calculation = Excel.XlCalculation.xlCalculationAutomatic;
でできることが分かった。
セルに書き込む間、画面更新を無効、手動計算にしたが、
期待したほどの速度改善ができなかった。
実験にしてみたが、マクロによる同様の書き込み処理に比べ約20%
遅い程度なので見方によってはかなり健闘しているとも言えるが、
ロジックを改良してもっとパフォーマンスを上げたい。
良いアイデアは無いだろうか。

投稿者:ひろし

編集 履歴 (0)

"エクセル 配列"を、Insider.NETから検索
@IT > Insider.NET > Insider.NET 会議室 > ディレクトリ > Office > Excel/CSV
Excelへの転送処理
配列のデータをEXCELにペーストする
VB.VETで、処理速度を早くしたいのですが


投稿者:Jitta

編集 履歴 (0)

VSTO+C#でもこの手法が使えればパフォーマンスの更なる向上が
期待できます。ご回答ありがとうございます。

投稿者:ひろし

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。