使用foreach遍历自定义可枚举类型的时候为什么并不要求自定义类型实现 IEnumerable和IEnumerator?

崔鹏飞 2009-05-08 09:28:18
就是这些代码:
把:IEnumerable和:IEnumerator注释掉依然可以用。如果并不强制要求实现这两个接口,那么foreach怎么知道我的自定义类型里面有用于返回枚举器的方法GetEnumerator()呢?这是不是违反了强类型约束?



using System;
using System.Collections;

public class Person
{
public Person(string fName, string lName)
{
this.firstName = fName;
this.lastName = lName;
}

public string firstName;
public string lastName;
}

public class People //: IEnumerable
{
private Person[] _people;
public People(Person[] pArray)
{
_people = new Person[pArray.Length];

for (int i = 0; i < pArray.Length; i++)
{
_people[i] = pArray[i];
}
}

public PeopleEnum GetEnumerator()
{
return new PeopleEnum(_people);
}
}

public class PeopleEnum //: IEnumerator
{
public Person[] _people;


int position = -1;

public PeopleEnum(Person[] list)
{
_people = list;
}

public bool MoveNext()
{
position++;
return (position < _people.Length);
}

public void Reset()
{
position = -1;
}

public object Current
{
get
{
try
{
return _people[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
}

class App
{
static void Main()
{
Person[] peopleArray = new Person[3]
{
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};

People peopleList = new People(peopleArray);
foreach (Person p in peopleList)
{
Console.WriteLine(p.firstName + " " + p.lastName);
}
Console.Read();
}
}

...全文
863 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
Tragedy 2012-01-17
  • 打赏
  • 举报
回复
经典!!!!
崔鹏飞 2009-05-08
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 qldsrx 的回复:]
好神奇,难道这就是C#的智能感知?反编译生成的程序看看,编译器有没有自动添加接口。
[/Quote]没有,用Reflector打开看了一下,没有自动添加接口。
聖少俊 2009-05-08
  • 打赏
  • 举报
回复
学习
vwxyzh 2009-05-08
  • 打赏
  • 举报
回复
CSharp Language Specification中(安装过visual studio的都可以找到):
foreach 语句用于枚举一个集合的元素,并对该集合中的每个元素执行一次相关的嵌入语句。
foreach-statement:
foreach ( local-variable-type identifier in expression ) embedded-statement
foreach 语句的 type 和 identifier 声明该语句的迭代变量 (iteration variable)。如果以 local-variable-type 形式给定 var 关键字,则称该迭代变量为隐式类型化的迭代变量 (implicitly typed iteration variable),并假定其类型为 foreach 语句的元素类型,如下面所指定。迭代变量相当于一个其范围覆盖整个嵌入语句的只读局部变量。在 foreach 语句执行期间,迭代变量表示当前正在为其执行迭代的集合元素。如果嵌入语句试图修改迭代变量(通过赋值或 ++ 和 -- 运算符)或将迭代变量作为 ref 或 out 参数传递,则将发生编译时错误。
foreach 语句的编译时处理首先确定表达式的集合类型 (collection type)、枚举器类型 (enumerator type) 和元素类型 (element type)。此确定过程按如下进行:
• 如果 expression 的类型 X 是数组类型,则存在从 X 到 System.Collections.IEnumerable 接口(因为 System.Array 实现此接口)的隐式引用转换。集合类型 (collection type) 是 System.Collections.Ienumerable 接口,枚举器类型 (enumerator type) 是 System.Collections.Ienumerator 接口,而元素类型 (element type) 是数组类型 X 的元素类型。
• 否则,确定类型 X 是否具有相应的 GetEnumerator 方法:
o 在带有标识符 GetEnumerator 和不带类型参数的类型 X 上执行成员查找。如果成员查找没有产生匹配项,产生了多义性,或者产生了不是方法组的匹配项,请按如下所述检查可枚举的接口。建议在成员查找产生除方法组外的任何匹配项或没有产生匹配项的情况下发出警告。
o 使用产生的方法组和空的参数列表执行重载决策。如果重载决策产生了不适用的方法、多义性或者单个最佳方法(但该方法是静态的或非公共的),请按如下所述检查可枚举的接口。建议在重载决策产生除无歧义的公共实例方法外的任何方法或没有产生适用方法的情况下发出警告。
o 如果 GetEnumerator 方法的返回类型 E 不是类、结构或接口类型,则将产生错误,并且不再执行进一步的操作。
o 在带有标识符 Current 和不带类型参数的 E 上执行成员查找。如果成员查找没有产生匹配项,结果是错误的或者是除允许读取的公共实例属性外的任何项,则将产生错误并且不再执行进一步的操作。
o 在带有标识符 MoveNext 和不带类型参数的 E 上执行成员查找。如果成员查找没有产生匹配项,结果是错误的或者是除方法组外的任何项,则将产生错误并且不再执行进一步的操作。
o 使用空的参数列表对方法组执行重载决策。如果重载决策产生了不适用的方法、多义性、单个最佳方法(但该方法是静态的或非公共的)或者其返回类型不是 bool,则将产生错误并且不再执行进一步的操作。
o 集合类型 (collection type) 为 X,枚举器类型 (enumerator type) 为 E,而元素类型 (element type) 为 Current 属性的类型。

• 否则,检查可枚举的接口:
o 如果恰好有一种类型 T,以致存在从 X 到接口 System.Collections.Generic.IEnumerable<T> 的隐式转换,则集合类型 (collection type) 为此接口,枚举器类型 (enumerator type) 为接口 System.Collections.Generic.IEnumerator<T>,元素类型 (element type) 为 T。
o 否则,如果存在多个此种类型 T,则将产生错误并且不再执行进一步的操作。
o 否则,如果存在从 X 到 System.Collections.IEnumerable 接口的隐式转换,则集合类型 (collection type) 为此接口,枚举器类型 (enumerator type) 为接口 System.Collections.IEnumerator,元素类型 (element type) 为 object。
o 否则,将产生错误并且不再执行进一步的操作。
上述步骤如果成功,将无歧义地产生集合类型 C、枚举器类型 E 和元素类型 T。以下形式的 foreach 语句
foreach (V v in x) embedded-statement
然后扩展为:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
ivws_19 2009-05-08
  • 打赏
  • 举报
回复
System.Collections
qldsrx 2009-05-08
  • 打赏
  • 举报
回复
好神奇,难道这就是C#的智能感知?反编译生成的程序看看,编译器有没有自动添加接口。
vrhero 2009-05-08
  • 打赏
  • 举报
回复
补充一句...泛型参数约束也得靠基类或接口来约束...
vrhero 2009-05-08
  • 打赏
  • 举报
回复
不可以...只能用泛型参数约束...

除非...你自己写个C#编译器,那你就可以定义自己的新语法了...
崔鹏飞 2009-05-08
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 vrhero 的回复:]
这是原因不是现象...C#编译器的规则,foreach只需要对象具有GetEnumerator、MoveNext、Reset和Current()成员,不需要接口...
[/Quote]那我可以模仿这种特性吗?比如说我定义一个方法,它接受一个参数,不限制参数的类型但是限定参数必须有一个某个特定方法。可以这样吗?
vrhero 2009-05-08
  • 打赏
  • 举报
回复
这是原因不是现象...C#编译器的规则,foreach只需要对象具有GetEnumerator、MoveNext、Reset和Current()成员,不需要接口...
崔鹏飞 2009-05-08
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 vrhero 的回复:]
原因很简单...MSDN中有明确的说明...

在 C# 中,集合类不一定要从 IEnumerable 和 IEnumerator 继承以便与 foreach 兼容。只要此类具有必需的 GetEnumerator、MoveNext、Reset 和 Current() 成员,就可 foreach 与一起使用。省略接口有一个好处:您可以将 Current 的返回类型定义得比 Object 更为明确,从而提供类型安全。
[/Quote]这是现象,其原因?
光宇广贞 2009-05-08
  • 打赏
  • 举报
回复
没有,你不知道吗?C#的数组本身就是 IEnumerator……

int [] a 将被译为 IEnumerator<System.Int32> ……

你把 _people 数组返回了……当然可以 foreach ……
辛鹤 2009-05-08
  • 打赏
  • 举报
回复
我姓区不姓区 2009-05-08
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 vrhero 的回复:]
原因很简单...MSDN中有明确的说明...

在 C# 中,集合类不一定要从 IEnumerable 和 IEnumerator 继承以便与 foreach 兼容。只要此类具有必需的 GetEnumerator、MoveNext、Reset 和 Current() 成员,就可 foreach 与一起使用。省略接口有一个好处:您可以将 Current 的返回类型定义得比 Object 更为明确,从而提供类型安全。
[/Quote]
up
wuyq11 2009-05-08
  • 打赏
  • 举报
回复
在使用一般集合和自定义类型时,实际上是通过调用GetEnumerator方法获取了枚举器之后,通过循环进行遍历的。
wuyq11 2009-05-08
  • 打赏
  • 举报
回复
不实现IEnumerable接口,写一个名字为GetEnumerator并且返回类型为IEnumerator的方法就是可以的
vrhero 2009-05-08
  • 打赏
  • 举报
回复
就可 foreach 与一起使用
------------
就可与 foreach 一起使用...这个小错误原文如此,中文MSDN的bug...
vrhero 2009-05-08
  • 打赏
  • 举报
回复
原因很简单...MSDN中有明确的说明...

在 C# 中,集合类不一定要从 IEnumerable 和 IEnumerator 继承以便与 foreach 兼容。只要此类具有必需的 GetEnumerator、MoveNext、Reset 和 Current() 成员,就可 foreach 与一起使用。省略接口有一个好处:您可以将 Current 的返回类型定义得比 Object 更为明确,从而提供类型安全。
ztenv 2009-05-08
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 cuipengfei1 的回复:]
引用 9 楼 lianshaohua 的回复:
应该会自动匹配到:GetEnumerator()这个方法,你可以把这个方法先屏蔽,看看还能不能运行?
屏蔽掉确实不行了,但是我就是奇怪,微软在开发的时候是怎么在内部实现foreach的呢?不做类型限制?
那岂不是意味着可以把任何类型写入foreach?但是随便写一个进去IDE又会在编译期报错....

搞不明白啊....
[/Quote]

改掉名字不知道还行不行,
lsfv00011 2009-05-08
  • 打赏
  • 举报
回复
关注结论.
加载更多回复(7)

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧