配列とGetEnumetorのお話
- 2012-05-29
LINQ書いていますか?LINQでデータベース活用とか見出しにありますが、データベース活用といいつつ、その内実は100% LINQ to Objectsです。じゃあデータベースに何で問い合わせやってるの?というと(禁則事項です)。さて、そんなわけで毎日LINQ書いているわけですが、それとは全く関係なく、配列が結構困ったちゃん。例えば以下のような、配列を包んだコレクションを提供したいとします。
public class WrappedCollection<T>
{
T[] source;
public WrappedCollection(T[] innerSource)
{
this.source = innerSource;
}
}
で、T[]なsourceを元にメソッドを幾つか提供する、と。それ自体はないこともないと思われます。さて、Collectionを名乗っているので、IEnumerable<T>であってほしいですよね?
なぜだ、って、配列のGetEnumeratorの戻り値はIEnumeratorなのです。IEnumerator<T>ではなくて。マジで!マジで。さて、どうしようかしら、と。foreach(var item in source) yield return item; をすれば解決ですが、そんなダサいことはやりたくない。正解は、AsEnumerableです。
public class WrappedCollection<T> : IEnumerable<T>
{
T[] source;
public WrappedCollection(T[] innerSource)
{
this.source = innerSource;
}
public IEnumerator<T> GetEnumerator()
{
return source.AsEnumerable().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
これだけで解決。やったね、AsEnumerableは偉大で奥深いなあ。さて、以前にDeep Dive AsEnumerableで書きましたが、AsEnumerableの実態はただのキャストなので
return ((IEnumerable<T>)source).GetEnumerator();
でもOKです。
さて、それらの中身ですが
var source = Enumerable.Range(1, 10).ToArray();
var e1 = source.GetEnumerator();
var e2 = source.AsEnumerable().GetEnumerator();
// System.Array+SZArrayEnumerator
Console.WriteLine(e1.GetType());
// System.SZArrayHelper+SZGenericArrayEnumerator`1[System.Int32]
Console.WriteLine(e2.GetType());
といった具合に、型が違うと渡ってくるEnumeratorも違うようですね。これ自体は別にスペシャルな機能ではなく明示的なインターフェイスの実装をした時の挙動、ではありますが、まあ配列周りはそもそもに色々とややこしいですからね。私みたいなゆとりなんて、SZって何だよクソが(single-dimension zero-baseの意味だそうで)とか思ってしまいます。
まとめ
IEnumerable<T>じゃないコレクションは逝ってよし。つまりMatchCollectionは何で.NET 4.5になっても手を加えてくれないんだよぅううううぅぅぅ。
XboxInfoTwit - ver.2.4.0.3
- 2012-05-13
Xbox.comが内部的にちょっと変わっていて動かなくなっていたので、それに対応しました。前々から報告されている不具合などに関する修正などは一切入ってません、すみません。最近割と普通に忙しくて全く手をかけられない状態でして……。落ち着いたらその時には必ず。