forとforeach、どちらのループを使用するのがいいのか考えてみた！

</br>
正直プログラム経験がまだまだ浅いから間違いはあるかも。ご指摘下さればありがたいです。
Qiitaに不慣れなため、見にくかったらごめんなさい！
</br>
##きっかけ
>https://akinow.livedoor.blog/archives/52474053.html
</br>

そもそものきっかけは、上記の記事でforeachの使用を控えるような内容を見たからだった。
(後々分かったが、これはUnity5.5で解決済みだった)
</br>
</br>
軽量化に力を入れていきたい身としては、重いとかメモリの効率が～なんて話は聞き捨てならない。
たとえ解決済みだとしても、気になる。一度気になりだしたら止まらない。
</br>
・・・まあ、結果。ハマりましたよ。
見事にループ処理の沼にハマったので、同じような疑問を感じた人と、未来の自分用のメモとして、結局何がどういいのかまとめてみる。
</br>
</br>
コンパイル結果は、[sharplab](https://sharplab.io/)にお世話になりました。
内部分のコードなんかは[actual implementation](https://referencesource.microsoft.com/)を参照させていただきました。
##配列編
</br>
コンパイル用に以下のようなコードを作成したので、コンパイルしてみる。
sumに配列の中身を足していく処理だ。
</br>
コンパイルしたいだけなので、sumがリセットされてないとか、配列に何も入っていないとか。
その辺は見逃してほしい・・・。
</br>

```c#
int[] TestArray = new int[100];
int sum = 0;

//forの処理
for (int i = 0; i < TestArray.Length; i++)
{
    sum += TestArray[i];
}

//foreachの処理
foreach (int i in TestArray)
{
    sum += i;
}
```

↓　コンパイル

```c#
int[] TestArray = new int[100];
int sum = 0;

//forの処理
int num = 0;
while (num < TestArray.Length)
{
    sum += array[num];
    num++;
}

//foreachの処理
int[] array2 = TestArray;
int num2 = 0;
while (num2 < array2.Length)
{
    int num3 = array2[num2];
    sum += num3;
    num2++;
}

```

</br>
上記のような結果が出力された。
</br>
</br>
</br>
**・・・おんなじやん！**
</br>
</br>
配列におけるループの内部処理は、単純にint型の変数を一つ用意して、ループ毎に足し、配列の大きさより大きくなったら、whileから抜けるだけのようだ。
</br>
まあ、強いて言えばここで配列が参照されているぐらいか。

```c#
int[] array2 = TestArray;
```

ちなみに配列は参照型なので、**コピーはされない**。上記のように書いても、渡されるのは配列のアドレス情報のみ。この程度なら問題はない。
</br>
もしも知らない！気になった！って方はその辺は論点がずれてしまうので、以下サイト様がわかりやすいと思う。

>https://ufcpp.net/study/csharp/oo_reference.html#type-category

</br>
さて、結果としては、やってることが同じならforを使うメリットは無い。
そもそも、foreachは糖衣構文（ 要は読み書きのしやすさを重要視されている処理 ）だから、**優先してforeachを使いたい。**


##List編

Listを回す際に、どちらを使用すべきか。
</br>
配列に対してのコンパイル結果は、分かりやすく非常に簡単な結果であったが、ListはEnumeratorというものが関わってくるため、少々厄介だったりする。
</br>
まずはコンパイルしてみる。内容はさっきと同じ。

```c#
List<int> TestList = new List<int>();
int sum = 0;

//forの処理
for (int i = 0; i < TestList.Count; i++)
{
    sum += TestList[i];
}

//foreachの処理
foreach (int i in TestList)
{
    sum += i;
}
```
↓　コンパイル

```c#
List<int> list = new List<int>();
int sum = 0;

//forの処理
int num = 0;
while (num < list.Count)
{
    sum += list[num];
    num++;
}

//foreachの処理
List<int>.Enumerator enumerator = list.GetEnumerator();
try
{
    while (enumerator.MoveNext())
    {
      int current = enumerator.Current;
      sum += current;
    }
}
finally
{
    ((IDisposable)enumerator).Dispose();
}

```

ちょっとごちゃごちゃしてきたので、混乱するかもしれないが順番に紐解いていこう。
まず、forの部分は先ほどの配列とやり方は変わっていないので、言わなくても分かると思う。
</br>
</br>
問題はforeachの方だろう。
</br>
まず、Listは幾つかのインターフェースを継承しているのだが、以下の行でその中の一つであるIEnumerable内の唯一のメソッドであるGetEnumerator()というメソッドにアクセスしている。

```c#
List<int>.Enumerator enumerator = list.GetEnumerator();
```

####GetEnumerator()が一体何をするメソッドなのか。

では、GetEnumerator()とはなんなのか。
</br>
簡単にまとめるとGetEnumerator()というメソッドは、List内にあるEnumeratorという構造体に自身の情報を設定して返すだけのメソッドだ。
</br>
もう少し詳しく言うならば、**GetEnumerator()を呼び出した際に、Enumeratorのコンストラクタにアクセスし、渡されたList、初期index、渡された時点での_version、currentを設定**し、設定したEnumeratorを返すメソッドだ。

####IEnumerator内のメソッド

次に以下の処理に注目してもらいたい。

```c#
    while (enumerator.MoveNext())
    {
      int current = enumerator.Current;
      sum += current;
    }
```

次に疑問に思うのは、MoveNext()とは何者なのかという点だろう。
**MoveNext()はEnumeratorが継承するインターフェースの一つであるIEnumerator<T>内のbool型のメソッド**だ。
こいつは簡単に言うと、List内の要素数が超えたらループを抜けて、要素を超えていない場合はループを続けるという処理が内部的に行われている。
</br>
もう少し深掘りしてみよう。
MoveNext()を覗いてみると、以下のようになっている。

```c#
public bool MoveNext() 
{
    List<T> localList = list;
 
    if (version == localList._version && ((uint)index < (uint)localList._size)) 
    {                                                     
        current = localList._items[index];                    
        index++;
        return true;
    }
    return MoveNextRare();
}
 
private bool MoveNextRare()
{                
    if (version != list._version) 
    {
      ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
    }
 
    index = list._size + 1;
    current = default(T);
    return false;                
}
```

これだけでは、訳が分からないかもしれないので、順番に見ていこう。
</br>
まず、以下の部分で、**Listのバージョンが合っているか。現在探索している要素がList内の要素数を超えていないか。**を調べている。
もし、条件が合っていた場合、current変数に現在の要素を設定し、探索位置をずらし、ループは続くのでtrueを返している。

```c#
    if (version == localList._version && ((uint)index < (uint)localList._size)) 
    {                                                     
        current = localList._items[index];                    
        index++;
        return true;
    }
```

では、条件が満たされなかった場合はどうなるのか。
その場合は、MoveNextRare()に入る。
</br>
**MoveNextRare()の処理は、まずversionが合っているか確認し、合っていなかった場合はエラーを吐く。**
もしあっていた場合は、currentを初期化してfalseを返すことでループを終了します。

```c#
private bool MoveNextRare()
{                
    if (version != list._version) 
    {
      ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
    }
 
    index = list._size + 1;
    current = default(T);
    return false;                
}
```

これがだいたいのforeachのList探索の概要です。
</br>
</br>
では、結局どっちをつかうのがいいのか？
以下のサイト様を見た所、速度面ではforの方がforeachよりも早い。

>https://takap-tech.com/entry/2020/10/20/234610

メモリ面から見ても、GetEnumerator()でEnumeratorを生成している分、forの方がいいように感じられるが、私個人の見解としては、そこまで気にするほどではないように感じた。
</br>
よっぽど、速度を重要視する場合や、メモリ効率を考えたい！といった場合でない限りは、糖衣構文であるforeachの使用を避ける程ではないように思う。
まあ、この程度であれば好みの範疇かな？

##結果

2021/04/21修正：配列の結果がforになってました！！！ごめんんささい！！

**配列：foreach**
**List：基本的にforeachでいい。よっぽど気にするようであればfor**
</br>
以上！
</br>
</br>
</br>
ご指摘、ご意見おまちしておりまｓ・・・
</br>
</br>
</br>
めｔｙくちゃ参考にさせていただきましたサイト様
>https://qiita.com/NCT48/items/d8394d587e9fee969ca9