2

Stateクラスを実装することが可能かどうか疑問に思っていたIEnumerable<Person>のでIEnumerable<City>、foreachを介して州に住むすべての人々とすべての都市を取得できました。これを言ってもコンパイルされません:(Error 1 'ConsoleApplication1.City' does not implement interface member 'System.Collections.IEnumerable.GetEnumerator()'奇妙な)...これがコードです:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections;

namespace ConsoleApplication1
{
    class Person
    {
    }

    class City : IEnumerable<Person>
    {
        // City has citizens:
        Person[] citizens;

        IEnumerator<Person> IEnumerable<Person>.GetEnumerator()
        {
            foreach (Person p in citizens)
                yield return p;
        }
    }

    class State : IEnumerable<Person>, IEnumerable<City>
    {
        // State has cities:
        City[] cities;

        IEnumerator<Person> IEnumerable<Person>.GetEnumerator()
        {
            foreach (City c in cities)
                foreach (Person p in c)
                    yield return p;
        }

        IEnumerator<City> IEnumerable<City>.GetEnumerator()
        {
            foreach (City c in cities)
                yield return c;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            State s = new State();
            foreach (Person p in s) ;
            foreach (City c in s) ;
        }
    }
}
4

3 に答える 3

10

問題は、IEnumerable<T>実装する必要があることですIEnumerable(非汎用バージョン)。GetEnumerator()両方の呼び出しを実装する必要があります。

Stateそうは言っても、列挙するものをクラスで決定する必要があるため、これは非常にトリッキーになります。個人的には、1 つのクラスで 2 回実装することは避けIEnumerable<T>、代わりに列挙型をメソッドとして返します。

class State : IEnumerable<City>
{
    public IEnumerable<Person> GetPeople()
    {
      // return people...

一般に、何かを 2 つの別々の型の列挙にしようとするのは、実際には設計上の欠陥だと思います。State を次のように実装する方が良いでしょう:

public class State
{
    public IEnumerable<City> Cities { get { // return cities...

    public IEnumerable<People> People { get { // return people...

これには、使用方法を (わずかに) 変更して、次のようにする必要があります。

foreach(Person person in theState.People)
{
    // ....

State個人的には、これは と の両方にとってより良いアプローチだと思いますCity。私はこれを次のように書きます:

using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{

    class Person
    {
    }

    class City
    {
        // City has citizens:
        Person[] citizens;

        public IEnumerable<Person> People
        {
            get
            {
                return citizens;
            }
        }
    }

    class State : IEnumerable<Person>, IEnumerable<City>
    {
        // State has cities:
        City[] cities;

        public IEnumerable<City> Cities
        {
            get
            {
                return cities;
            }
        }

        public IEnumerable<Person> AllPeople
        {
            get
            {
                return Cities.SelectMany(c => c.People);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            State s = new State();
            foreach (Person p in s.AllPeople) { /* Do something */ }
            foreach (City c in s.Cities) { /* Do something */ } 
        }
    }
}

都市には人がいますが、それ自体ではなく、人などではないため、これははるかに明確です。

于 2012-08-08T20:05:21.157 に答える
2

リードとセバスチャンの両方が指摘している理由から、非ジェネリックメソッドも実装する必要があるため、これは実際には興味深い質問です。ただし、実装できるのは1回のみであり、を必要とする2つのインターフェースがあります。より良い設計では、状態にIEnumerableを実装することはまったくないかもしれませんが、IEnumerablesを公開するPeopleとCitiesの2つのプロパティがあります。これももう少し便利な(発見可能な)APIだと思います。

于 2012-08-08T20:11:01.760 に答える
2

非ジェネリック バリアントも実装する必要があります。System.Collections.IEnumerable は、ジェネリック型引数のないものです!

System.Collections.Generic.IEnumerable.GetEnumerator の明示的なインターフェイス実装を 1 つ追加し、例外をスローさせます。非ジェネリック インターフェイスを正しく実装する方法はありません。

于 2012-08-08T20:05:06.437 に答える