C# のコレクション使用法のガイドライン　その１　配列

今日は、.NET のクラスライブラリ設計の本を読んだ。Collection 周りが理解できていないのがわかったので、設計方針をリストアップして、分かっていないところをつぶしたいと思う。

# 配列

## DO: パブリックなAPIにおいては、配列よりもコレクションの使用を選択する

```csharp

public Collection<Item> Items { get; set; }
```

コレクションのほうが、高いユーザビリティを誇るため。ただし、低レベルのAPIを開発しているときは、配列を使用するほうが良いときがある。配列の要素のアクセスは、コレクションより高速であるから。メモリを抑えて高速化したいときなど。

また、バイトの場合は、コレクションより配列。（これは上記の理由と思われる。）

```csharp
public byte[] ReadBytes() {...
```

## 多次元配列の代わりに、ジャグ配列を使用する

ジャグ配列というのは、配列の要素が配列であるもの。多次元配列と比較すると、メモリ消費量が有利で、CLRは、ジャグ配列におけるインデックス操作も最適化してくれるので、早そうです。これは、本当かしらんので試してみましょう。

メモリがどう違うか確かめてみます。

```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CollectionSpike
{

    class NewInt
    {
        public int Value { get; private set; }
        public NewInt(int value)
        {
            Value = value;
        }
    }
    class OldInt
    {
        public int Value { get; private set; }
        public OldInt(int value)
        {
            Value = value;
        }
    }
    class ArraySpike
    {
        public void Exec()
        {
            OldInt[][] jaggedArray =
            {
                new OldInt[] {new OldInt(1), new OldInt(2), new OldInt(3), new OldInt(4)},
                new OldInt[] {new OldInt(5), new OldInt(6), new OldInt(7)},
                new OldInt[] {new OldInt(8)},
                new OldInt[] {new OldInt(9)}
            };

            NewInt[,] multiDimArray =
            {
                {new NewInt(1), new NewInt(2), new NewInt(3), new NewInt(4)},
                {new NewInt(5), new NewInt(6), new NewInt(7), new NewInt(0)},
                {new NewInt(8), new NewInt(0), new NewInt(0), new NewInt(0)},
                {new NewInt(9), new NewInt(0), new NewInt(0), new NewInt(0)}
            };

            var sw = new System.Diagnostics.Stopwatch();

            var times = 10000000;

            sw.Start();
            for (int i = 0; i < times; i++)
            {
                foreach (var items in jaggedArray)
                {
 
                    foreach (var item in items)
                    {
                        if (item.Value == 8)
                        {

                        }
                    }
                }
            }
            sw.Stop();
           Console.WriteLine($"jagged.foreach.foreach: {sw.Elapsed}");

            sw.Restart();
            for (int i = 0; i < times; i++)
            {
                jaggedArray.SelectMany(x => x).Select(x => x.Value % 2 == 0);
            }
            sw.Stop();
            Console.WriteLine($"jagged.SelectMany.Select: {sw.Elapsed}");

            sw.Restart();
            for (int i = 0; i < times; i++)
            {
                foreach (var item in multiDimArray)
                {
                    if (item.Value == 8)
                    {

                    }
                }
            }

            sw.Stop();
            Console.WriteLine($"multiDim foreach: {sw.Elapsed}");

            Console.WriteLine($"jagged: {jaggedArray} multi: {multiDimArray}");
        }
    }
}

```

実行結果

```
jagged.foreach.foreach: 00:00:03.2893552
jagged.SelectMany.Select: 00:00:01.8337845
multiDim foreach: 00:00:09.8060043
```
ん、確かに。しかし、Linq が一番早いのは意外！優秀ですね。ちなみに、多次元配列だと、Linqはつかえませんでした。
じゃあ、メモリ。Visual Studio で、ヒープのスナップショットを撮って、比較してみます。ジャグ配列は単純に、ある型としてヒープが消費されていますが、多次元配列は、新たに多次元配列の型が出来てメモリを消費しているようです。たしかに、倍ぐらい多次元配列のほうがメモリを食っています。今はLinq もあるし、確かにジャグ配列ですな。

<img width="1368" alt="memory.png" src="https://qiita-image-store.s3.amazonaws.com/0/3470/1d3bb286-2f13-3c89-efbd-ae197581b8c2.png">

長くなったので、まずは第一弾ここまで。