7

2021年了,`IEnumerator`、`IEnumerable`接口还傻傻分不清楚?

 3 years ago
source link: http://network.51cto.com/art/202101/640569.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

IEnumerator、IEnumerable这两个接口单词相近、含义相关,傻傻分不清楚。

入行多年,一直没有系统性梳理这对李逵李鬼。

最近本人在怼着why神的《其实吧,LRU也就那么回事》,方案1使用数组实现LRU,手写算法涉及这一对接口,借此时机覆盖这一对难缠的冤家。

IEnumerator

IEnumerator、IEnumerable接口有相似的名称,这两个接口通常也在一起使用,它们有不同的用途。

IEnumerator接口为类内部的集合提供了迭代方式, IEnumerator 要求你实现三个方法:

MoveNext方法:该方法将集合索引加1,并返回一个bool值,指示是否已到达集合的末尾。

Reset方法:它将集合索引重置为其初始值-1,这会使枚举数无效。

Current方法: 返回position位置的当前对象

IEnumerable

IEnumerable接口为foreach迭代提供了支持,IEnumerable要求你实现GetEnumerator方法。

public IEnumerator GetEnumerator()

{

return (IEnumerator)this;

}

该用哪一个接口?

仅凭以上辞藻,很难区分两个接口的使用场景。

IEnumerator接口定义对类中的集合类型对象的迭代方式,

IEnumerable接口允许使用foreach循环进行枚举。

因此IEnumerable接口的GetEnumerator方法会返回一个IEnumerator接口。要实现IEnumerable,你还必须实现IEnumerator。

“从英文词根上讲:

IEnumerator接口代表了枚举器,里面定义了枚举方式,是名词。

IEnumerable接口代表该对象具备了可被枚举的性质,是形容词。

总之,如果您想提供对foreach的支持,那么就先让对象可枚举,再谈论枚举方式,也就是说实现这两个接口。

最佳实践

在嵌套类中实现IEnumerator,这样你可以创建多个枚举器。

为IEnumerator的Current方法提供异常处理。

为什么要这么做?

如果集合的内容发生变化,则reset方法将被调用,紧接着当前枚举数无效,您将收到一个IndexOutOfRangeException异常(其他情况也可能导致此异常)。所以执行一个Try…Catch块来捕获这个异常并引发InvalidOperationException异常, 提示在迭代时不允许修改集合内容。

“这也正是我们常见的在foreach 里面尝试修改迭代对象会报InvalidOperationException异常的原因。

下面以汽车列表为例实现IEnumerator IEnumerable接口

using System;

using System.Collections;

namespace ConsoleEnum

{

public class cars : IEnumerable

{

private car[] carlist;

//Create internal array in constructor.

public cars()

{

carlist= new car[6]

{

new car("Ford",1992),

new car("Fiat",1988),

new car("Buick",1932),

new car("Ford",1932),

new car("Dodge",1999),

new car("Honda",1977)

};

}

//private enumerator class

private class MyEnumerator:IEnumerator

{

public car[] carlist;

int position = -1;

//constructor

public MyEnumerator(car[] list)

{

carlist=list;

}

private IEnumerator getEnumerator()

{

return (IEnumerator)this;

}

//IEnumerator

public bool MoveNext()

{

position++;

return (position < carlist.Length);

}

//IEnumerator

public void Reset()

{

position = -1;

}

//IEnumerator

public object Current

{

get

{

try

{

return carlist[position];

}

catch (IndexOutOfRangeException)

{

throw new InvalidOperationException();

}

}

}

} //end nested class

public IEnumerator GetEnumerator()

{

return new MyEnumerator(carlist);

}

}

}

在foreach cars的时候,可以明显看到

foreach语法糖初次接触可枚举的cars, 实际会访问cars实现的 GetEnumerator()方法,拿到迭代器

foreach每次迭代,实际会访问迭代器的Current属性


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK