Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9314

forとforeach、どちらのループを使用するのがいいのか考えてみる(現在List編は書きかけです…)

$
0
0
正直プログラム経験がまだまだ浅いから間違いはあるかも。ご指摘下さればありがたいです。 ※すみません。訳あってList編の途中のまま投稿しています。ご了承ください。月曜日にはまた時間が取れそうなので、月曜日には書き終える予定です。 きっかけ https://akinow.livedoor.blog/archives/52474053.html そもそものきっかけは、上記の記事でforeachの使用を控えるような内容を見たからだった。 (後々分かったが、これはUnity5.5で解決済みだった) 軽量化に力を入れていきたい身としては、重いとかメモリの効率が~なんて話は聞き捨てならない。 たとえ解決済みだとしても、気になる。一度気になりだしたら止まらない。 ・・・まあ、結果。ハマりましたよ。 見事にループ処理の沼にハマったので、同じような疑問を感じた人と、未来の自分用のメモとして、結局何がどういいのかまとめてみる。 コンパイル結果は、sharplab様にお世話になりました。 配列編 コンパイル用に以下のようなコードを作成したので、コンパイルしてみる。 sumに配列の中身を足していく処理だ。 コンパイルしたいだけなので、sumがリセットされてないとか、配列に何も入っていないとか。 その辺は見逃してほしい・・・。 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; } ↓ コンパイル 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++; } 上記のような結果が出力された。 ・・・おんなじやん! 配列におけるループの内部処理は、単純にint型の変数を一つ用意して、ループ毎に足し、配列の大きさより大きくなったら、whileから抜けるだけのようだ。 まあ、強いて言えばここで配列が参照されているぐらいか。 int[] array2 = TestArray; ちなみに配列は参照型なので、コピーはされない。上記のように書いても、渡されるのは配列のアドレス情報のみ。この程度なら問題はない。 もしも知らない!気になった!って方はその辺は論点がずれてしまうので、以下サイト様がわかりやすいと思う。 https://ufcpp.net/study/csharp/oo_reference.html#type-category さて、結果としては、やってることが同じならforを使うメリットは無い。 そもそも、foreachは糖衣構文( 要は読み書きのしやすさを重要視されている処理 )だから、優先してforeachを使いたい。 List編 Listを回す際に、どちらを使用すべきか。 配列に対してのコンパイル結果は、分かりやすく非常に簡単な結果であったが、ListはEnumeratorというものが関わってくるため、少々厄介だったりする。 まずはコンパイルしてみる。内容はさっきと同じ。 ( ListにはLINQのCount()関数と、List内のcount変数があるが、変数でやる場合の方が個人的に多いし、こちらの方が処理が速いためこちらを使用している。他にも理由はあるけど後述する。) 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; } ↓ コンパイル 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の部分は先ほどの配列とやり方は変わっていないので、言わなくても分かると思う。 問題はforeachの方だろう。 まず、Listは幾つかのインターフェースを継承しているのだが、以下の行でその中の一つであるIEnumerable内の唯一のメソッドであるGetEnumerator()というメソッドにアクセスしている。 List<int>.Enumerator enumerator = list.GetEnumerator(); GetEnumerator()が一体何をするメソッドなのか。 では、GetEnumerator()とはなんなのか。 簡単にまとめるとGetEnumerator()というメソッドは、List内にあるEnumeratorという構造体に自身の情報を設定して返すだけのメソッドだ。 もう少し詳しく言うならば、GetEnumerator()を呼び出した際に、Enumeratorのコンストラクタにアクセスし、渡されたList、初期index、渡された時点での_version、currentを設定し、設定したEnumeratorを返すメソッドだ。 IEnumerator内のメソッド 次に以下の処理に注目してもらいたい。 while (enumerator.MoveNext()) {   int current = enumerator.Current;   sum += current; } 次に疑問に思うのは、MoveNext()とは何者なのかという点だろう。 MoveNext()はEnumeratorが継承するインターフェースの一つであるIEnumerator内のbool型のメソッドだ。 内部では主に、

Viewing all articles
Browse latest Browse all 9314

Latest Images

Trending Articles