.NET Coreのソースを読む！第一回 List<T> パート2

# はじめに

[.NET Coreのソースを読む！第一回 List<T> パート1](https://qiita.com/kutarou197/items/48416013dff136f2587e)の続きです。

# ソースリンク

https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/Collections/Generic/List.cs

# ソースを読む！

## プロパティ

```csharp
        public int Capacity
        {
            get
            {
                return _items.Length;
            }
            set
            {
                if (value < _size)
                {
                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
                }

                if (value != _items.Length)
                {
                    if (value > 0)
                    {
                        T[] newItems = new T[value];
                        if (_size > 0)
                        {
                            Array.Copy(_items, 0, newItems, 0, _size);
                        }
                        _items = newItems;
                    }
                    else
                    {
                        _items = s_emptyArray;
                    }
                }
            }
        }
```

Capacityは、List内部で保持している配列の長さになりますね。Listの長さではなく、内部でデータを実際に格納している配列の長さです。Addしていって、Capacityを超えそうになったら、自動で拡張するようになっていますね。前回も記載しましたが、最初からデータ量が多いと分かっている場合は、Capacityをコンストラクタで指定するのが定石ですね。

「_size」が実際のデータの長さなので、それを超える場合は、エラーにするのですね。そもそも、Listのインスタンスを作成した後に、Capacityが変えれるのは知りませんでした。Capacityを変えると、内部で新たな配列を作成するので、ちゃんと理解しておかないといけませんね。

valueに0が指定されたら「s_emptyArray」をちゃんと初期化時に使っていますね。共通ライブラリなので、少しでもメモリを節約する努力だと思うのですが、どこの誰が、Capacityに0を指定するのでしょうね。。。

```csharp
        // Read-only property describing how many elements are in the List.
        public int Count => _size;
```

これで「_size」の値を読み取り専用で公開できるのですね。
6.0からの機能で、「expression-bodied function member」というのですね。
私の現場では、5.0なので、これは使ったことありません。

しかし、公開はCountで、内部は_sizeってのはなんででしょうか？
最初に_sizeにしちゃって、変えようと思ったら、シリアライズ対策のために変えれなかったのかな。
あと、IList<T>ではCountプロパティで、LINQにもCountメソッド（拡張メソッドだけど）があるから紛らわしいですね。

```csharp
        bool IList.IsFixedSize => false;
        bool ICollection<T>.IsReadOnly => false;
        bool IList.IsReadOnly => false;
        bool ICollection.IsSynchronized => false;
```

List<T>は、固定長ではなく、読み取り専用でもなく、スレッドセーフでもないということですね。

おお、`bool IList.IsFixedSize => false;`みたいに、特定のインタフェースで参照したときしか参照できない方法でオーバーライドしてますね。`IList.IsFixedSize`とすると、IListインタフェースで参照したときしか、IsFixedSizeプロパティが参照できなくなりますね。
しかし、このC#の機能は私は一度も使ったことがないのですが、標準ライブラリではガンガン使っているのですね。

```csharp
        object ICollection.SyncRoot
        {
            get
            {
                if (_syncRoot == null)
                {
                    Interlocked.CompareExchange<object>(ref _syncRoot, new object(), null);
                }
                return _syncRoot;
            }
        }
```

SyncRootプロパティは、誰かが同期処理をするときに使うのかな？使ったことないけど。

https://msdn.microsoft.com/ja-jp/library/system.collections.icollection.syncroot(v=vs.110).aspx

`Interlocked.CompareExchange`は、便利そうですね。詳細は、以下の「（4） 値を比較し、等しければ指定した別の値を代入する」に説明がありました。

http://www.atmarkit.co.jp/ait/articles/0505/25/news113_3.html

ロックして、値を初期化するときに便利そうですが、逆に分かりにくいかもしれませんね。

```csharp
        public T this[int index]
        {
            get
            {
                // Following trick can reduce the range check by one
                if ((uint)index >= (uint)_size)
                {
                    ThrowHelper.ThrowArgumentOutOfRange_IndexException();
                }
                return _items[index];
            }

            set
            {
                if ((uint)index >= (uint)_size)
                {
                    ThrowHelper.ThrowArgumentOutOfRange_IndexException();
                }
                _items[index] = value;
                _version++;
            }
        }
```

インデクサーですね。
範囲外の場合はエラーにしますね。
インデクサーでは自動で拡張してくれませんよ！！お気をつけて。
自動で拡張するのはAddメソッドでっせ。

```csharp
        object IList.this[int index]
        {
            get
            {
                return this[index];
            }
            set
            {
                ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);

                try
                {
                    this[index] = (T)value;
                }
                catch (InvalidCastException)
                {
                    ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
                }
            }
        }
```

おお、Genericではない型で参照されたときのインデクサーの実装は、分けているのですね。

次回はスタティックメソッドかな。

# ライセンス

色々ソースを載せているので、念のためライセンスへリンクしておきます。

https://github.com/dotnet/corefx/blob/master/LICENSE.TXT