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

# はじめに

以下の続きです。

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

- [.NET Coreのソースを読む！第一回 List<T> パート2](https://qiita.com/kutarou197/items/a493458d16b05bf8ebbb)

# ソースリンク

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

# ソースを読む！

## スタティックメソッド

```csharp
        private static bool IsCompatibleObject(object value)
        {
            // Non-null values are fine.  Only accept nulls if T is a class or Nullable<U>.
            // Note that default(T) is not equal to null for value types except when T is Nullable<U>. 
            return ((value is T) || (value == null && default(T) == null));
        }
```

Genericではないインタフェースにも対応しているので、Genericではないインタフェースで参照されたとき（何ていえばいいのかな？）のContains・IndexOf・Removeメソッドで型をチェックするためのメソッドのようです。

.Net Coreのソースにはコメントが少ないけど、ここにコメントが書いているということは大事なことなのかもしれません。
Tの型がNullが非許容（基本データ型と構造体）でNullableインタフェースを実装していない場合には、`default(T) == null`がfalseになりますね。なので、TがNull非許容型の場合には、引数がnullは必ずfalseになりますね。

## インスタンスメソッド

```csharp
        // Adds the given object to the end of this list. The size of the list is
        // increased by one. If required, the capacity of the list is doubled
        // before adding the new element.
        //
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Add(T item)
        {
            _version++;
            T[] array = _items;
            int size = _size;
            if ((uint)size < (uint)array.Length)
            {
                _size = size + 1;
                array[size] = item;
            }
            else
            {
                AddWithResize(item);
            }
        }

        // Non-inline from List.Add to improve its code quality as uncommon path
        [MethodImpl(MethodImplOptions.NoInlining)]
        private void AddWithResize(T item)
        {
            int size = _size;
            EnsureCapacity(size + 1);
            _size = size + 1;
            _items[size] = item;
        }

        int IList.Add(object item)
        {
            ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item);

            try
            {
                Add((T)item);
            }
            catch (InvalidCastException)
            {
                ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T));
            }

            return Count - 1;
        }
```

Addメソッド（ジェネリック版）にある`[MethodImpl(MethodImplOptions.AggressiveInlining)]`は、コンパイル時（C#のコンパイルではなく、JITコンパイルの時のようです）に、インライン化をしてもらいやすくするための指定のようです。

参考
[[雑記] インライン化](http://ufcpp.net/study/csharp/structured/miscinlining/)

Addメソッドは、大量に呼び出される可能性があるので、インライン化による性能向上を目的にしているのでしょうね。

しかし、私が理解が出来ていないのが、AddWithResizeメソッドの`[MethodImpl(MethodImplOptions.NoInlining)]`で、インライン化を禁止しているところです。
コメントには、`Non-inline from List.Add to improve its code quality as uncommon path`とあるから、AddメソッドとIList.Addメソッドでの共通化のために、AddWithResizeメソッドがあるけど、IList.Addメソッドからの呼び出しでは、インライン化されないからなのか、インライン化されないようにするためのなか・・・。すいません、分かりません。

インデクサー側にも同じ処理があったのですが、`if ((uint)size < (uint)array.Length)`は、性能のためのようです。
私は気づかなかったのですが、以下の @NetSeed さんの記事にちょうど記載されていました。

参考
[0,n)のレンジチェックを1回で済ませる](https://qiita.com/NetSeed/items/1ee03c3a0648e79439b2)

```csharp
        public void AddRange(IEnumerable<T> collection)
            => InsertRange(_size, collection);
```

6.0から追加された「ラムダ式本体によるメソッドの記述」という方法ですね。
InsertRangeメソッドもpublicであり、こんなメソッドがあるとは知りませんでした。

```csharp
        public ReadOnlyCollection<T> AsReadOnly()
            => new ReadOnlyCollection<T>(this);
```

読み取り専用のCollectionに変換するメソッドです。
ReadOnlyCollectionはスレッドセーフになるので、もう変更しないListの情報をキャッシュするときには、これを使った方がよいですね。

今回は、ここまで。

しかし、まだList<T>のソースの半分も終わっていない・・・。
全部網羅するのは諦めようかな。

# ライセンス

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

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