关于c#:我如何实现IEnumerable< T>

How do I implement IEnumerable<T>

我知道如何实现非泛型IEnumerable,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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,但我不知道如何实现它。

如果我将using System.Collections.Generic;添加到我的使用指令中,然后更改:

1
class MyObjects : IEnumerable

到:

1
class MyObjects : IEnumerable<MyObject>

然后右键单击IEnumerable并选择Implement Interface => Implement Interface,Visual Studio会帮助添加以下代码块:

1
2
3
4
IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator()
{
    throw new NotImplementedException();
}

这次从GetEnumerator();方法返回非泛型IEnumerable对象不起作用,那么我在这里放什么呢?现在,当CLI在foreach循环期间尝试通过我的数组枚举时,它将忽略非泛型实现并直接指向泛型版本。


如果您选择使用通用集合,如List而不是ArrayList,您会发现List将提供您可以使用的通用和非通用枚举器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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();
    }
}


您可能不希望显式实现IEnumerable(这是您所展示的)。

通常的模式是在显式实现IEnumerable时使用IEnumerable的getEnumerator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();
    }
}


为什么要手动操作?yield return自动处理迭代器的整个过程。(我也在我的博客上写过,包括一个编译器生成的代码)。

如果你真的想自己做,你也必须返回一个通用的枚举器。您将无法再使用ArrayList,因为它不是通用的。改为List。当然,假设您的集合中只有EDOCX1(或派生类型)类型的对象。


如果使用泛型,请使用list而不是arraylist。列表中正好有您需要的getEnumerator方法。

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

把我的名单变成一个List是一种选择


请注意,IEnumerable完全由System.Collections实现,因此另一种方法是将MyObjects类从System.Collections派生为基类(文档):

System.Collections: Provides the base class for a generic collection.

之后我们可以自己提出建议,推翻提供定制行为的虚拟System.Collections方法(仅适用于ClearItemsInsertItemRemoveItemSetItem以及EqualsGetHashCodeToString。与设计不易扩展的List不同。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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驾驶,这种情况很少发生。参见用法。