2007年05月24日

GetEnumerator() vs. GetEnumerable()

  
C#で、

 public class MyClass {
public IEnumerable<int> GetEnumerator() {
yield return 1;
yield return 2;
yield return 3;
yield return 4;
}
}

というコードを書けば、

 MyClass mc = new MyClass();
foreach ( int i in mc.GetEnumerator() ) {
Console.WriteLine(i);
}

と foreach 文が書ける。最も手軽な反復子の書き方だ。

でも、2つのメソッドで、値を順に取り出したいときなどforeach 文 が使えない場面もある。
こんなときは、MoveNext(), Current メソッド使うのだが、ちょっと、困った。

 IEnumerator<int> e = mc.GetEnumerator().GetEnumerator();
while ( e.MoveNext() ) {
Console.WriteLine(e.Current);
}

これで、一応目的は達したのだが、GetEnumerator().GetEnumerator();
という部分は、何かおかしい。

そもそも、IEnumerable<> を返しているのに、GetEnumerator() という名前をつけていることが変だ。

やはり、

public IEnumerable<int> GetEnumerable() {
yield return 1;
yield return 2;
yield return 3;
yield return 4;
}


と GetEnumerable() が良さそうだ。

でも、

GetEnumerable を Google で日本語のページを検索すると、たった数件しかヒットしない。
何でだろう?

よくよく考えたら、戻り値が、IEnumerableの方を直すべきだった。GetEnumerator()なんだから、IEnumerator を返すメソッドにしないとね。


※コメントの内容を反映して一部書き直してます。


この記事へのコメント
こういうときはclassを作らなくても、↓のように書くほうが早いからGetEnumerable()と書く人が少ないのではないかと思います。

public static IEnumerable<int> Range( int start, int count )
{
if ( count < 0 ) throw new ArgumentOutOfRangeException( "count" );
for ( int i = 0; i < count; i++ )
yield return start + i;
}

ちなみにこれはLINQのまねです。
Posted by siokoshou at 2007年05月25日 07:36
siokoshouさん、コメントありがとうございます。いつもBlog拝見しています。

僕の例も、クラスを作らないコードという意味だったのですが...
つまり、インターフェースを実装しているわけではないので、

> public IEnumerable<int> GetEnumerable() {

と、GetEnumerable という名前をつける必要はなく、

public IEnumerable<int> Honarara() {

と、取り出す要素が何かがわかるようなメソッド名を
書く人が多い、ということですかね。

-----
タイトルは、GetEnumerator() vs. GetEnumerator() ではなく、
GetEnumerator() vs. GetEnumerable()
でした。(^^; 直しときました。

Posted by Gushwell at 2007年05月25日 09:09
あっすみません、早とちりしました…。オブジェクトを作らなくても、ってことに訂正します。
クラスとメソッドをstaticにしたほうがよいかなと思います。MyClassのオブジェクトを生成する意味がないのと、LINQがそういう使い方をしているので合わせておくと将来も使いやすいと思います。
メソッド名はそうですね、意味あるものにしたほうがいいと思います。(こういうときのクラス名ってすごく迷ってしまいますが(^^;)

> siokoshouさん、コメントありがとうございます。いつもBlog拝見しています。

えぇっ!ビックリです。ありがとうございます(^^)
Posted by siokoshou at 2007年05月25日 16:26
メソッド名が GetEnumerator っていうくらいだから、戻り値の型も IEnumerator にするっていうもんじゃあないですか。
というか、yield return を使ったメソッドで戻り値の型を IEnumerator ではなく IEnumerable にするメリットが私には見出せません……。
Posted by まじかんと at 2007年05月25日 21:12
siokoshouさん
LINQについてはまだ、知識不足でなんともいえませんが、この例では確かに static にしたほうが良いですね。


まじかんとさん
冷静になって考えれば(って冷静でなくても分かるだろうって突込みがはいりそうですが)、その通りですね。
foreach にしろ、GetNext() にしろ、IEnumeratorでいいわけだから、yield return を使ったメソッドの場合は、わざわざ、IEnumrableにする必要はないですね。

Posted by gushwell at 2007年05月25日 23:57
まじかんとさん、はじめまして。#Calc、すばらしいですね!サイトにもたびたびお世話になっております。

> というか、yield return を使ったメソッドで戻り値の型を IEnumerator ではなく IEnumerable にするメリットが私には見出せません……。

IEnumerator<>だとforeachで使えません。以下はコンパイルエラーになります。

static IEnumerable<T> A<T>( IEnumerator<T> it )
{
foreach ( T item in it ) // NG
yield return item;
}

一方、IEnumerable<>は可能です。

static IEnumerable<T> B<T>( IEnumerable<T> it )
{
foreach ( T item in it )
yield return item;
}

なので、私は可能な限りIEnumerable<>でやり取りするようにしています。
Posted by siokoshou at 2007年05月26日 04:05
 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/50802752
この記事へのトラックバック
それで、元々の「GetEnumerable を Google で日本語のページを検索すると、たった数件しかヒットしない。何でだろう?」という話ですが、上で gushwell さんが「IEnumerableを実装する、しないにかかわらず、GetEnumerator()を実装していさえすれば、(中略)と書ける。」と書...
Re: GetEnumerator() vs. GetEnumerable()【まじかんと雑記】at 2007年05月26日 14:04