IEnumerable <T>를 구현하는 방법


124

다음과 같이 비 제네릭 IEnumerable을 구현하는 방법을 알고 있습니다.

using System;
using System.Collections;

namespace ConsoleApplication33
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObjects myObjects = new MyObjects();
            myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 };
            myObjects[1] = new MyObject() { Foo = "World", Bar = 2 };

            foreach (MyObject x in myObjects)
            {
                Console.WriteLine(x.Foo);
                Console.WriteLine(x.Bar);
            }

            Console.ReadLine();
        }
    }

    class MyObject
    {
        public string Foo { get; set; }
        public int Bar { get; set; }
    }

    class MyObjects : IEnumerable
    {
        ArrayList mylist = new ArrayList();

        public MyObject this[int index]
        {
            get { return (MyObject)mylist[index]; }
            set { mylist.Insert(index, value); }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return mylist.GetEnumerator();
        }
    }
}

그러나 IEnumerable에는 제네릭 버전이 IEnumerable<T>있지만 구현 방법을 알 수 없습니다.

using System.Collections.Generic;using 지시문에 추가 한 다음 변경하면 :

class MyObjects : IEnumerable

에:

class MyObjects : IEnumerable<MyObject>

그런 다음을 마우스 오른쪽 버튼으로 클릭 하고을 IEnumerable<MyObject>선택 Implement Interface => Implement Interface하면 Visual Studio가 다음 코드 블록을 유용하게 추가합니다.

IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator()
{
    throw new NotImplementedException();
}

GetEnumerator();메서드 에서 제네릭이 아닌 IEnumerable 개체를 반환하는 것은 이번에는 작동하지 않으므로 여기에 무엇을 입력해야합니까? CLI는 이제 일반이 아닌 구현을 무시하고 foreach 루프 동안 내 배열을 열거하려고 할 때 일반 버전으로 곧장 향합니다.

답변:


149

당신과 같은 일반적인 수집, 사용하기로 선택한 경우 List<MyObject>대신을 ArrayList, 당신은이 것을 찾을 수 있습니다 List<MyObject>당신이 사용할 수있는 모두 일반 및 제네릭이 아닌 열거를 제공합니다.

using System.Collections;

class MyObjects : IEnumerable<MyObject>
{
    List<MyObject> mylist = new List<MyObject>();

    public MyObject this[int index]  
    {  
        get { return mylist[index]; }  
        set { mylist.Insert(index, value); }  
    } 

    public IEnumerator<MyObject> GetEnumerator()
    {
        return mylist.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

1
비 제네릭 구현에서 반환 this.GetEnumerator()과 단순히 반환 사이에 차이점이 GetEnumerator()있습니까?
— Tanner Swett 2015 년

1
@TannerSwett 차이가 없습니다.
— Monroe Thomas

상속 할 수도 Collection<MyObject>있고 사용자 지정 코드를 작성할 필요가 없습니다.
— John Alexiou

@ ja72 이미 다른 기본 클래스에서 상속 중이고 Collection <MyObject>에서 상속 할 수없는 경우 어떻게해야합니까?
— Monroe Thomas

1
@MonroeThomas-그러면 답변에 표시된대로 래핑 List<T>하고 구현 IEnumerable<T>해야합니다.
— John Alexiou

69

당신은 아마도 (당신이 보여준) 명시적인 구현을 원하지 않을 것입니다 IEnumerable<T>.

일반적인 패턴은 다음을 명시 적으로 구현할 때 IEnumerable<T>' 를 사용 GetEnumerator하는 것입니다 IEnumerable.

class FooCollection : IEnumerable<Foo>, IEnumerable
{
    SomeCollection<Foo> foos;

    // Explicit for IEnumerable because weakly typed collections are Bad
    System.Collections.IEnumerator IEnumerable.GetEnumerator()
    {
        // uses the strongly typed IEnumerable<T> implementation
        return this.GetEnumerator();
    }

    // Normal implementation for IEnumerable<T>
    IEnumerator<Foo> GetEnumerator()
    {
        foreach (Foo foo in this.foos)
        {
            yield return foo;
            //nb: if SomeCollection is not strongly-typed use a cast:
            // yield return (Foo)foo;
            // Or better yet, switch to an internal collection which is
            // strongly-typed. Such as List<T> or T[], your choice.
        }

        // or, as pointed out: return this.foos.GetEnumerator();
    }
}

2
SomeCollection <T>가 IEnumerable <T>를 아직 구현하지 않았거나 열거 형 시퀀스에서 반환되기 전에 각 요소에 대해 변환하거나 다른 처리를 수행해야하는 경우 좋은 솔루션입니다. 이러한 조건 중 어느 것도 참이 아니면 this.foos.GetEnumerator ();를 반환하는 것이 더 효율적일 것입니다.
— Monroe Thomas

@MonroeThomas : 동의했습니다. OP가 사용하는 컬렉션에 대한 단서가 없습니다.
— user7116 2012-07-02

8
좋은 지적. 그러나 구현 IEnumerable은 중복됩니다 ( IEnumerable<T>이미 상 속됨).
— rsenna dec

IList의이 중복 인터페이스를 구현 같은 @rsenna 사실이다, 그러나 그것은 읽기 쉽의 마음에도 .NET 인터페이스를 유지 - 공중 인터페이스은 IList :은 ICollection, IEnumerable을
— 데이빗 Klempfner

@Backwards_Dave 나는 실제로 (여전히) 불필요한 소음을 추가하기 때문에 가독성이 떨어 졌다고 생각하지만 당신이 말한 것을 이해하고 그것이 유효한 의견이라고 생각합니다.
— rsenna

24

수동으로하는 이유는 무엇입니까? yield return반복자를 처리하는 전체 프로세스를 자동화합니다. (저는 제 블로그 에도 썼습니다. 컴파일러 생성 코드를 포함하여 .)

정말로 직접하고 싶다면 제네릭 열거 자도 반환해야합니다. 일반이 아니기 때문에 ArrayList더 이상 사용할 수 없습니다 . List<MyObject>대신 a로 변경하십시오 . 물론 MyObject컬렉션 에 유형 (또는 파생 유형)의 개체 만 있다고 가정합니다 .


2
+1, 선호되는 패턴은 일반 인터페이스를 구현하고 비 일반 인터페이스를 명시 적으로 구현하는 것입니다. 수익률은 일반 인터페이스에 대한 가장 자연스러운 솔루션입니다.
— user7116 2012-07-02

5
OP가 열거 자에서 일부 처리를 수행하지 않는 한 yield return을 사용하면 다른 상태 시스템의 오버 헤드가 추가됩니다. OP는 기본 제네릭 컬렉션에서 제공하는 열거자를 반환해야합니다.
— Monroe Thomas

@MonroeThomas : 당신이 맞아요. 에 대해 쓰기 전에 질문을 매우주의 깊게 읽지 않았습니다 yield return. 사용자 지정 처리가 있다고 잘못 생각했습니다.
— Anders Abel

4

제네릭으로 작업하는 경우 ArrayList 대신 List를 사용하십시오. List에는 필요한 GetEnumerator 메서드가 있습니다.

List<MyObject> myList = new List<MyObject>();

0

mylist를으로 만드는 List<MyObject>것은 하나의 옵션입니다.


0

에 IEnumerable<T>의해 구현 된 allready System.Collections또 다른 접근 방식은 MyObjects클래스를 System.Collections기본 클래스 ( documentation ) 로 파생하는 것입니다 .

System.Collections : 제네릭 컬렉션에 대한 기본 클래스를 제공합니다.

우리는 나중에 우리 자신의 된 구현 가상 재정의 할 수 있습니다 System.Collections사용자 정의 동작을 제공하는 방법을 (만 해당 ClearItems, InsertItem, RemoveItem,와 SetItem함께 Equals, GetHashCode및 ToString에서 Object). 달리List<T>쉽게 확장 할 수 있도록 설계되지 않은 다릅니다.

예:

public class FooCollection : System.Collections<Foo>
{
    //...
    protected override void InsertItem(int index, Foo newItem)
    {
        base.InsertItem(index, newItem);     
        Console.Write("An item was successfully inserted to MyCollection!");
    }
}

public static void Main()
{
    FooCollection fooCollection = new FooCollection();
    fooCollection.Add(new Foo()); //OUTPUT: An item was successfully inserted to FooCollection!
}

collection드물게 발생하는 맞춤형 수집 행동이 필요한 경우에만 운전을 권장합니다. 사용법을 참조하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.