我读过 Eric 的文章 here关于 foreach 枚举以及 foreach 可以工作的不同场景
为了防止旧的 C# 版本进行装箱,C# 团队启用了 foreach 的 duck typing 以在非 Ienumerable 集合上运行。(一个公共(public) GetEnumerator
返回具有 public MoveNext
和 Current
属性就足够了(.
所以,Eric 写了一个 sample :
class MyIntegers : IEnumerable
{
public class MyEnumerator : IEnumerator
{
private int index = 0;
object IEnumerator.Current { return this.Current; }
int Current { return index * index; }
public bool MoveNext()
{
if (index > 10) return false;
++index;
return true;
}
}
public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
但我认为它有一些拼写错误 (在 Current
属性实现中缺少 get 访问器) 导致它无法编译(我已经通过电子邮件发送给他)。
无论如何这是一个工作版本:
class MyIntegers : IEnumerable
{
public class MyEnumerator : IEnumerator
{
private int index = 0;
public void Reset()
{
throw new NotImplementedException();
}
object IEnumerator.Current {
get { return this.Current; }
}
int Current {
get { return index*index; }
}
public bool MoveNext()
{
if (index > 10) return false;
++index;
return true;
}
}
public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
好的。
根据 MSDN :
A type
C
is said to be acollection type
if it implements theSystem.Collections.IEnumerable
interface or implements thecollection pattern
by meeting all of the following criteria:
C 包含带有签名 GetEnumerator() 的公共(public)实例方法,它返回结构类型、类类型或接口(interface)类型,在下文中称为 E。
E 包含一个带有签名 MoveNext() 和返回类型 bool 的公共(public)实例方法。
E 包含一个名为 Current 的公共(public)实例属性,它允许读取当前值。此属性的类型称为集合类型的元素类型。
好的。让我们将文档与 Eric 的示例相匹配
Eric 的样本是据说是一个集合类型
,因为它确实实现了System.Collections.IEnumerable
接口(interface)(尽管是明确的)。但它不是(!)集合模式
,因为第 3 条:MyEnumerator
不是公共(public)实例属性命名当前。
MSDN说:
If the collection expression is of a type that implements the collection pattern (as defined above), the expansion of the foreach statement is:
E enumerator = (collection).GetEnumerator();
try {
while (enumerator.MoveNext()) {
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
Otherwise , The collection expression is of a type that implements System.IEnumerable (!), and the expansion of the foreach statement is:
IEnumerator enumerator =
((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
while (enumerator.MoveNext()) {
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
问题 #1
Eric 的示例似乎既没有实现
collection pattern
或 System.IEnumerable
- 因此它不应匹配上面指定的任何条件。那么为什么我仍然可以通过以下方式迭代它:
foreach (var element in (new MyIntegers() as IEnumerable ))
{
Console.WriteLine(element);
}
问题 #2
为什么我必须提到 new MyIntegers() as IEnumerable
?它已经是 Ienumerable (!!) 甚至在那之后,编译器不是已经通过强制转换自己完成了这项工作:
((System.Collections.IEnumerable)(collection)).GetEnumerator() ?
就在这里:
IEnumerator enumerator =
((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
while (enumerator.MoveNext()) {
...
那么为什么它仍然要我提到 Ienumerable 呢?
最佳答案
MyEnumerator does not has the required public methods
是的 - 或者更确切地说,如果 Current
是公开的。所需要的只是它具有:
- 一个公共(public)的、可读的
当前
属性 - 一个没有类型参数返回
bool
的公共(public)MoveNext()
方法
基本上,这里缺少 public
只是另一个错字。事实上,这个例子并没有达到它的目的(防止装箱)。它使用 IEnumerable
实现,因为您正在使用 new MyIntegers() as IEnumerable
- 所以表达式类型是 IEnumerable
,它只使用整个界面。
您声称它没有实现 IEnumerable
(顺便说一句,它是 System.Collections.IEnumerable
),但它确实实现了,使用显式接口(interface)实现。
根本不实现 IEnumerable
来测试这类东西是最简单的:
using System;
class BizarreCollection
{
public Enumerator GetEnumerator()
{
return new Enumerator();
}
public class Enumerator
{
private int index = 0;
public bool MoveNext()
{
if (index == 10)
{
return false;
}
index++;
return true;
}
public int Current { get { return index; } }
}
}
class Test
{
static void Main(string[] args)
{
foreach (var item in new BizarreCollection())
{
Console.WriteLine(item);
}
}
}
现在,如果您将 Current
设为私有(private),它将无法编译。
关于C# `foreach` 行为 - 说明?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31342847/