LoginSignup
3
2

More than 5 years have passed since last update.

EPPlusでちょっとばっかし注意すること その2

Last updated at Posted at 2016-08-02

どー言うことだってばよ

追加で検証するため、EPPlusのソースコード読んでいたら、おやと思うことがあったので、とりあえずまとめておく。

何が起きるのか

下記のような操作をしたとする。


using System;
using System.IO;
using OfficeOpenXml;

namespace EPPlusSurvey
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var package = new ExcelPackage(new FileInfo("Sample.xlsx")))
            {
                var sheet = package.Workbook.Worksheets[1];

                //A1:B10の矩形範囲にはすべて値が詰まっている。
                var range = sheet.Cells["A1:B10"];

                var foo = range.GetEnumerator();
                var bar = range.GetEnumerator();


                foo.MoveNext();
                foo.MoveNext();

                bar.MoveNext();
                bar.MoveNext();

                //こいつはTrueになる。
                Console.WriteLine(ReferenceEquals(foo, bar));

                //だもんで、foo及びbarをMoveNextした結果が、それぞれにに反映される。

                //foo:B2
                Console.WriteLine($"foo:{foo.Current.Address}");

                //bar:B2
                Console.WriteLine($"bar:{bar.Current.Address}");
            }
        }
    }
}

コメントでネタバレしてるけど、GetEnumeratorで取得できるEnumeratorが独立していない。
なので、上記サンプルで、foo及びbarに対する操作の結果が、それぞれに反映されてしまうことになる(つまり、MoveNextの結果が累積している)。
このような操作をするシナリオはそんなに無いだろうけど、ステートの保持を目的としてEnumeratorを保持して、長い間使い回したりすると、問題が起きるかも知れないのでご注意の程。

回避策

じゃあ、回避策はあるのかというと、range変数を使わずに、都度都度sheet.CellsプロパティでRangeを取得すれば回避できる。

using System;
using System.IO;
using OfficeOpenXml;

namespace EPPlusSurvey
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var package = new ExcelPackage(new FileInfo("Sample.xlsx")))
            {
                var sheet = package.Workbook.Worksheets[1];

                //A1:B10の矩形範囲にはすべて値が詰まっている。
                var rangeA = sheet.Cells["A1:B10"];
                var rangeB = sheet.Cells["A1:B10"];

                //Falseになる。
                Console.WriteLine(ReferenceEquals(rangeA, rangeB));

                var foo = rangeA.GetEnumerator();
                var bar = rangeB.GetEnumerator();

                foo.MoveNext();
                foo.MoveNext();

                bar.MoveNext();


                //当然こいつはFalseになる。
                Console.WriteLine(ReferenceEquals(foo, bar));

                //共々独立しているのでめでたしめでたし

                //foo:B1
                Console.WriteLine($"foo:{foo.Current.Address}");
                //bar:A1
                Console.WriteLine($"bar:{bar.Current.Address}");
            }
        }
    }
}

こんなケースはまれだろうけど、一般的なGetEnumeratorの戻り値と異なっているので、その点注意しないとマズいかと思って一応まとめてみた。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2