疑問メモ: IEnumerable<>の派生クラスで実装すべきGetEnumerator
次の本を読んでいたら、C#の言語機能を解説した章で、分からない箇所がありました。
2つのModelクラスがあります。1つは、Product。もう1つは、List
ShoppingCartは、IEnumerable<>を拡張しています。
Product.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace LanguageFeatures.Models { public class Product { public int ProductID { get; set; } public int Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { get; set; } } }
ShoppingCart.cs
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; namespace LanguageFeatures.Models { public class ShoppingCart: IEnumerable<Product> { public List<Product> Products { get; set; } // 1つ目のGetEnumerator public IEnumerator<Product> GetEnumerator() { return Products.GetEnumerator(); } // 2つ目GetEnumerator IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }
ShoppingCartクラスに、GetEnumeratorメソッドが2つ出てきます。
1つ目のGetEnumeratorがIEnumerator
なぜ2つの実装が必要なのかが分かりません。
検索したら解説記事を見つけました。
IEnumerable
を継承するには GetEnumerator()メソッドを実装します。 IEnumerator
GetEnumerator(); 加えて、 IEnumerator の GetEnumerator() メソッドも実装する必要があります。
IEnumerator GetEnumerator()
これは IEnumerator
が単に IEnumerator のジェネリック版というだけでなく、 IEnumerator を継承しているインターフェースだからです。 IEnumerator
を継承すると IEnumerator も継承することになります。 実際にはIEnumerator
C# インターフェース - IEnumerable(T) | プログラマーズ雑記帳の GetEnumerator() しかまず使われません。 しかし、両方の GetEnumerator() メソッドを実装しないとコンパイルに失敗してしまいます。
GetEnumeratorが2つ必要な理由が、分かりそうで分かりません分かったので、追記します。smogamiさん、tsurumaruさんにTwitterで教えていただきました。ありがとうございます。
- 2つ目のGetEnumeratorが内部で呼び出しているGetEnumeratorが、1つ目のGetEnumeratorではないかと推測するが、それは正しいか → 正しい
- もし正しければ、「GetEnumerator()」と書くだけで、1つ目、つまりIEnumerator
を返す方のメソッドが呼ばれるのはなぜか もし正しくなければ、2つ目のGetEnumeratorが内部で呼び出しているGetEnumeratorは何をしているのか
IEnumerator
このようにインタフェース名をつけて実装したメソッドは、インタフェースを介してしかアクセスできなくなります。したがって、2つ目のメソッドが呼び出しているのは、インタフェース名を特定しない実装、すなわち1つ目のGetEnumerator()です。
smogamiさんに教えてもらったページがとても分かりやすいです。
- 明示的なインターフェイスの実装 (C# プログラミング ガイド) http://msdn.microsoft.com/ja-jp/library/ms173157(v=vs.80).aspx
(追記ここまで)
ちなみに、利用側のコードは、IEnumerableをforeachで回すExtension Methodです。
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace LanguageFeatures.Models { public static class MyExtensionMethods { public static decimal TotalPrice(this IEnumerable<Product> productEnum) { decimal total = 0; foreach (Product prod in productEnum) { total += prod.Price; } return total; } } }