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, GetHashCodeToString에서 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.