導入
原則として、抽象化すると GC.Alloc します。
つまり、基底クラスの「IList」「IEnumerable」とかにキャストして foreach すると、 GC.Alloc します。
なぜ GC.Alloc する?
foreach は内部で GetEnumerator()
を実行しますが、
「IList」などの抽象的な世界では IEnumerator
が返ります。
これはインターフェースなので、ヒープ領域にメモリが確保されます。
抽象化しなければ問題ない
List とかはセーフです。
List は GetEnumerator()
されると、 IEnumerator
ではなく List<T>.Enumerator (構造体)
を返します。
構造体なので、スタックに確保されます。
foreach は GetEnumerator()
の戻り値が特定のメソッドを持っていればOKなので、構造体でも問題ないです。
配列は特別扱い
配列は特別に、コンパイラの最適化によってfor文に書き換えられます。
つまり、実は foreach していません。
気をつけるべき場面
次みたいな抽象化されたメソッドを作るとき。
自作Linqでよく落ち入る罠かも。
private void Hoge<T>(IList<T> collection);
最後に
ゼロアロケーションを目指してチューニングしていた時に、よく陥りました。
自分も昔は自作Linq最強!とかやっていたので、少し恥ずかしいです…。