为什么我们需要IEnumerator和IEnumerable?

4

好的,我刚刚在研究IEnumeratorIEnumerable。现在MSDN说这些东西的主要目的是遍历客户端集合。

好吧,我可以遍历自定义集合而不使用任何一个(或者至少我认为如此)。

namespace ienumerator
{

class person
{
    public person(string first, string second)
    {
        fname = first;
        lname = second;
    }
    public string fname { get; set; }
    public string lname { get; set; }
}

class person_list
{
    public person[] list;
    public person_list(person[] per)
    {
        list = new person[per.Length];
        for (int i = 0; i < per.Length; i++)
        {
            list[i] = per[i];
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        person[] names = new person[3]
        {
            new person("some","one"),
            new person("another","one"),
            new person("sss","ssdad"),
        };

        person_list list_of_names = new person_list(names);
        int i = 0;
        while (i < list_of_names.list.Length)
        {
            Console.WriteLine(list_of_names.list[i].fname);
            i++;
        }
        Console.Read();
    }
 }
}

那么我的理解有什么问题呢?上述内容给了我使用 IEnumeratorIEnumerable 实现后得到的相同结果。


请查看“foreach”:http://msdn.microsoft.com/zh-cn/library/vstudio/ttw7t8t6.aspx - kol
4
现在将列表的类型更改为LinkedList并尝试编译它。 - tukaef
@2kay 我猜可能有一些长度属性的问题吗? - Win Coder
在你的代码中,int i是一个简单(且暴露)的迭代器。 - H H
1
虽然题外话,但是你的变量名和命名规范需要改进。一个叫做“per”的参数不是一个好的名称,因为它没有任何意义。“person”则可以被理解。如今内存便宜且充足,节省三个字符在参数名称上并没有什么好处。 - David Klempfner
5个回答

18

我可以不使用接口来迭代自定义集合。

当然,可以迭代你自己的集合而不使用这个接口。如果你不能这样做,那么你该如何实现接口呢?

你作为代码实现者,实现接口并非为了你自己方便,而是为了代码使用者的方便。接口的意义在于:“请注意,客户端:你可以枚举这个集合的内容,而不必知道我是如何实现它的”。

这就是接口的目的:它提供了一个抽象层,使得代码使用者能够在不必理解其工作原理的情况下使用它。


6
你所编写的类将来有可能会被使用,也有可能永远不会被使用。如果你认为这个类将来永远不会被使用,那么这个类的功能如何并不重要,因为它永远不会被使用。如果你认为这个类将来会被使用一次或多次,那么使用这个类的人就是消费者。请注意,消费者可能是你未来的自己。现在做出有利于你未来的选择。 - Eric Lippert
2
@WinCoder:那就提出一个新问题吧!这就是这个网站存在的意义。简单来说,IEnumerator就像一本书,IEnumerable就像读者。你可以有很多读者阅读同一本书,而且每个读者都在不同的页面上。 - Eric Lippert
3
@WinCoder:场景是这样的:有人想使用你的类型,但不是你本人。他们的任务是在集合的成员之间进行迭代。你可以让他们使用标准的IEnumerable/IEnumerator技术,这样他们就不必学习一些新的、不同的技术来迭代你的集合,这是你的一种善意。 - Eric Lippert
4
@EricLippert 说的是"IEnumerator 就像一本书,而 IEnumerable 则像读者。" 这句话是否符合您的意图?听起来对我来说有些颠倒了。 - MarkPflug
古老的问题,但是... @MattWolf - 书和书签可能是更好的类比 - 许多人可以抓住同一本书并在停止阅读的地方留下书签(IEnumerator)。这也说明了为什么修改集合是一个问题 - 某人可以撕掉你的书签上的页面,现在它就丢失了... - Alexei Levenkov
显示剩余6条评论

2
在您的main函数中,您使用了person_list两个方面来迭代人员列表,即Length[]操作符,用于在特定位置获取集合的成员。
然而,如果您有一些容器或自定义集合不实现其中一个或两个呢?例如,逐行读取文件的类可能事先不知道集合的长度,因此不会定义Length。从队列读取消息的枚举器可能只能获取下一个消息,而不能获取例如第四个位置的消息[4]。在这两种情况下,您仍然可以正确“迭代”集合。 IEnumeratorIEnumerable 需要foreach正常工作所需的属性,没有其他属性,这就是为什么函数可以将它们用作要求,因为他们知道他们将能够迭代传递给他们的任何类型的集合。

现在这些是一些令人信服的论点。 - Win Coder

1

实际上,在C#中,自定义集合不需要实现 IEnumerableIEnumerator 接口。如果您通过传统方式迭代集合,则可以正常使用,但如果使用 foreach 迭代集合,则会出现问题。

另一个原因是,如果您想公开您的类以便 VB.NET 用户使用,则应考虑实现这两个接口。

请注意,IEnumerable 在非泛型和泛型集合中有所区别。非泛型属于 System.Collection,而泛型则属于 System.Collection.Generic 命名空间。


1

当然,您可以在不实现 IEnumerable 接口的情况下迭代自定义集合,但是迭代代码必须知道您的自定义集合。

如果您实现了 IEnumerable,那么迭代所有元素的代码就不需要知道您的 person_list 类。它使用实现 IEnumerable 的某个类的实例,如果以后将实际实例替换为其他也实现 IEnumerable 的内容,则仍将正常工作。


那么这意味着我不必为每种不同的集合编写while循环,对吗? - Win Coder

0

有很多方法可以迭代自定义序列。其中一种方法是使用 迭代器设计模式

迭代器模式用于统一遍历自定义序列(它可以是内存中的集合、生成的集合或其他内容)。在.NET世界中,这个模式的实现方式如下:我们有一个可迭代对象,它实现了IEnumerable接口(这向您类的任何客户表明您具有遍历内容的能力)。

我们还有一个迭代器本身(实现IEnumerator接口的对象),您可以将其视为指向可迭代对象中当前位置的“指针”。因为您可能有内部循环(即在同一时间内对同一对象进行多次迭代),所以必须将可迭代对象与迭代器分开。

基本上,有很多方法可以遍历特定序列,但迭代器模式使您能够“泛化”此方法,从而将遍历算法的特定实现隐藏在实现内部的调用者中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接