Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么GetEnumerator()存储在IEnumerator的单独接口中?_C#_Iterator_Ienumerable_Ienumerator - Fatal编程技术网

C# 为什么GetEnumerator()存储在IEnumerator的单独接口中?

C# 为什么GetEnumerator()存储在IEnumerator的单独接口中?,c#,iterator,ienumerable,ienumerator,C#,Iterator,Ienumerable,Ienumerator,我想知道为什么GetEnumerator()方法被排除在IEnumerator之外,并放在IEnumerable中。在我看来,在IEnumerator中保留所有枚举器方法更有意义 谢谢 ScottIEnumerable意味着对象是可以以线性方式迭代的数据集合或数据源IEnumerator是执行迭代的实际实现的接口。因为“IEnumerable”说“来,枚举我”(然后你说-how,给我枚举器),但是“IEnumerator”说“我可以枚举你的集合!”而你已经有了它,您不需要再获取更多信息。因为通常

我想知道为什么GetEnumerator()方法被排除在IEnumerator之外,并放在IEnumerable中。在我看来,在IEnumerator中保留所有枚举器方法更有意义

谢谢


Scott

IEnumerable
意味着对象是可以以线性方式迭代的数据集合或数据源
IEnumerator
是执行迭代的实际实现的接口。

因为“IEnumerable”说“来,枚举我”(然后你说-how,给我枚举器),但是“IEnumerator”说“我可以枚举你的集合!”而你已经有了它,您不需要再获取更多信息。

因为通常进行枚举的对象与被枚举的对象仅存在切向关系(如果有的话)。如果IEnumerable和IEnumerator是同一个接口,则无法共享它们,或者您需要在GetEnumerator上具有一些非类型参数,以便传入要枚举的对象。拆分它们允许枚举基础结构在不同类型之间共享。

问问自己“想象一下这是真的”

如果所有枚举方法都在一个接口上,那么两个调用方如何同时枚举同一个列表

有两个接口,一个说“你可以枚举我”,而另一个说“这是一个跟踪给定枚举任务的对象。”


IEnumerable
接口是一个工厂,可以创建任意多个
IEnumerator
对象。如何以及何时使用这些枚举数取决于消费者。

您在这里得到了非常好的答案。只是强调一下。枚举器保持状态,它跟踪正在枚举的集合中的当前对象。可通过IEnumerator.Current获得。它知道如何改变这种状态,IEnumerator.MoveNext()。保持状态需要一个单独的对象来存储状态。该状态不能轻松存储在集合对象中,因为只有一个集合对象,但可以有多个枚举数

我使用短语“轻松”,因为集合实际上可以跟踪其枚举数。毕竟,是对集合类的GetEnumerator()方法的调用返回了迭代器。在.NET framework中有一个集合类可以实现此功能,即Microsoft.VisualBasic.collection。它需要为其集合类实现VB6协定,该协定指定在枚举集合时更改集合是合法的。这意味着在修改集合时,它需要对创建的所有迭代器对象进行一些合理的修改


他们想出了一个很好的把戏,WeakReference可能就是从中得到启发的。请看反射器显示的代码。鼓舞人心的东西。进一步挖掘并在集合类中找到“版本”。绝妙的技巧。

在阅读了巴特·德斯米特(Bart De Smet)的文章后,我再也不相信严格要求拆分两个接口了

例如,在上面的链接中,IEnumerable/IEnumerator绑定到一个方法接口

Func<Func<Option<T>>>
Func
以及一些基本的实现

public static class FEnumerable
{
    public static Func<Func<Option<T>>> Empty<T>()
    {
        return () => () => new Option<T>.None();
    }

    public static Func<Func<Option<T>>> Return<T>(T value)
    {
        return () =>
        {
            int i = 0;
            return () =>
                i++ == 0
                ? (Option<T>)new Option<T>.Some(value)
                : (Option<T>)new Option<T>.None();
        };
    }

    ...

}

public static Func<Func<Option<T>>> Where<T>(this Func<Func<Option<T>>> source, Func<T, bool> filter)
{
    return source.Bind(t => filter(t) ? FEnumerable.Return(t) : FEnumerable.Empty<T>());
}
公共静态类FEnumerable
{
公共静态Func Empty()
{
return()=>()=>newoption.None();
}
公共静态函数返回(T值)
{
return()=>
{
int i=0;
return()=>
i++==0
?(选项)新选项。部分(值)
:(选项)新选项。无();
};
}
...
}
公共静态函数,其中(此函数源,函数筛选器)
{
返回source.Bind(t=>filter(t)?FEnumerable.return(t):FEnumerable.Empty();
}

+1。这是一个很好的例子,说明了为什么接口是分离的和离散的。另一个原因是同一类型集合可以以不同的方式枚举。想象一个二叉树——您可以执行前缀、中缀、后缀和宽度优先遍历(仅举几个例子)。保持接口分离允许通过一个简单的接口在同一个结构上执行不同类型的遍历。我想知道为什么没有一个更干净的集合(至少在Net4.0之前)可以做到这一点?枚举集合并有选择地删除元素似乎是一种常见的操作。@super:因为它从根本上是破坏性的。您可能能够补偿迭代器对集合的单个变异。但是,像移除一个元素并将其插入另一个位置这样的操作会破坏某些东西。迭代器将看到一个元素两次或根本看不到。除此之外,跟踪迭代器的所有活动实例是相当昂贵的。我可以理解,“普通”集合不支持此类功能,但集合设法具有有用的语义,如果它们在类中实现而没有其古怪之处,则更有用。人们不希望到处都有这样的语义,但有时它们很好。事实上,我希望看到更多的iEnumerator口味;应用程序可以根据所需的语义选择一个(一些会添加方法,另一些只会添加保证)。一个有用的方法是ipurgenumerator:对每个项调用一个谓词,如果为true,则删除。好吧,编写自己的谓词。你已经有了一个例子。