如何实现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循环中尝试通过数组枚举时直接进入通用版本。

Answers:


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

1
@TannerSwett没有区别。
门罗·托马斯

您最好继承自Collection<MyObject>,然后就不必编写任何自定义代码。
约翰·亚历克斯

@ ja72如果您已经从另一个基类继承并且不能从Collection <MyObject>继承怎么办?
门罗·托马斯

1
@MonroeThomas-然后您必须包装List<T>并实现IEnumerable<T>,如答案中所示。
John Alexiou '18

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();可能会更高效。
门罗·托马斯

@MonroeThomas:同意。不知道OP正在使用什么集合。
user7116

8
好点子。但是实现IEnumerable是多余的(IEnumerable<T>已经从它继承了)。
rsenna 2014年

@rsenna是正确的,但是请记住,即使是像IList这样的.NET接口也要冗余实现接口,这是为了提高可读性-公共接口IList:ICollection,IEnumerable
David Klempfner

@Backwards_Dave我实际上(仍然)认为它的可读性较差,因为它会增加不必要的噪音,但是我理解您的发言,并认为这是有效的观点。
rsenna '18

24

为什么要手动进行?yield return使处理迭代器的整个过程自动化。(我也在我的博客上写了有关它的内容,包括查看编译器生成的代码)。

如果您真的想自己做,则还必须返回一个通用枚举器。您将不能再使用,ArrayList因为这是非通用的。将其更改为一个List<MyObject>来代替。当然,这假定您的集合中只有类型MyObject(或派生类型)的对象。


2
+1,应注意,首选模式是实现通用接口并显式实现非通用接口。收益率回报是通用接口最自然的解决方案。
user7116

5
除非OP要在枚举器中进行某些处理,否则使用yield return只会增加另一个状态机的开销。OP应该只返回基础通用集合提供的枚举数。
门罗·托马斯

@MonroeThomas:你是对的。在撰写之前,我没有很仔细地阅读过这个问题yield return。我错误地认为有一些自定义处理。
安德斯·亚伯



0

请注意,IEnumerable<T>System.Collectionsso 实现的allready 已经是将您的MyObjects类派生System.Collections为基类(documentation):

System.Collections:提供通用集合的基类。

我们以后可以使我们自己实行重写虚拟System.Collections方法,以提供定制的行为(仅适用于ClearItemsInsertItemRemoveItem,和SetItem沿EqualsGetHashCode以及ToStringObject)。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仅在需要自定义收集行为的情况下才建议使用这种驱动,这种情况很少发生。见用法

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.