又是两个以rable和rator结尾的两个接口,每次看到这种定义我就会很头疼,很容易搞混掉,所以这次我要将自己的学习记录下来,慢慢的把它们给啃掉,好记性不如烂笔头,也方便今后自己的复习。
通常在编程中我们常常面临这样一个问题就是对一个集合的遍历通常也就是一个数组或是一个链表,比如说一个保存了整个班级名单的变量还是一个Person类型的数组,那我们该怎么去做。当然在C#中我们可以使用foreach语句来轻松的完成遍历:
foreach
(Person item
in
personArray)
{
//
...do something
}
但这里有个前提条件就是personArray对象类型它必须实现IEnumerable才能够使用foreach语句来进行遍历,看看IEnumerable接口的定义,它只有一个方法:
public
interface
IEnumerable
{
IEnumerator GetEnumerator();
}
而且该方法的返回类型正是IEnumerator接口,为什么要返回这么一个接口呢,同样看下它的定义:
public
interface
IEnumerator
{
bool
MoveNext();
object
Current {
get
; }
void
Reset();
}
这个接口有三个方法,最后一个方法暂时先不管,主要看前面两个,似乎有点明白了,从方法名字上来看MoveNext移动到下一个元素,如果有下一个元素就是返回true否则就是false。比如当前遍历到了某个同学,要是他不是最后一个说明还可以继续遍历返回true,如果是最后一个了说明不能在往下遍历,再往下可能就是别的班的同学了,此时返回一个false。在看下一个Current,它不是一个方法而是属性,只有一个get方法说明它是只读的,返回值是object可以是任意的对象。那么它的作用就显而易见了就是获取当前遍历位置下的元素了。从以上可以看出真正的遍历实现是由IEnumerator来实现的,IEnumerable规定了该类是可用foreach遍历的,且返回一个遍历的具体实现类IEnumerator。就好像学校过来检查要点名了,任课老师(IEnumerable)懒的自己一个一个点就吩咐记录委员(IEnumerator)来点名,记录委员就不停的MoveNext、MoveNext…点到的同学呢(Current)就喊一声到,一直点到最后一个同学(返回false)才停止。这时假如任课老师觉得人数不对来的人也太少了怎么就全到了呢,就要求重点(这里就相当于Reset了),无奈职责所在只好在foreach一遍…
具体代码如下:
namespace
Demo
{
class
Program
{
static
void
Main(
string
[] args)
{
Person[] array
=
{
new
Person { Name
=
"
叶华斌
"
, Age
=
23
},
new
Person { Name
=
"
王昌文
"
, Age
=
22
},
new
Person { Name
=
"
吴朝剑
"
, Age
=
21
}};
PersonArray pa
=
new
PersonArray(array);
foreach
(Person item
in
pa)
{
Console.WriteLine(
string
.Format(
"
姓名:{0},年龄{1}
"
, item.Name, item.Age));
}
}
}
public
class
PersonArray : IEnumerable
{
Person[] array;
public
PersonArray(Person[] array)
{
this
.array
=
array;
}
public
IEnumerator GetEnumerator()
{
return
new
PersonEnumerator(array);
}
}
public
class
PersonEnumerator : IEnumerator
{
Person[] array;
int
position;
public
PersonEnumerator(Person[] array)
{
this
.array
=
array;
this
.position
=
-
1
;
}
public
object
Current
{
get
{
if
(
-
1
<
position
&&
position
<
array.Length)
{
return
array[position];
}
else
{
throw
new
IndexOutOfRangeException();
}
}
}
public
bool
MoveNext()
{
position
++
;
return
position
<
array.Length;
}
public
void
Reset()
{
position
=
-
1
;
}
}
public
class
Person
{
public
string
Name {
get
;
set
; }
public
int
Age {
get
;
set
; }
}
}
输出结果:
值得一看的是反编译之后它的具体实现如何,来看下:
Person[] array;
PersonArray pa;
Person item;
Person
<>
g__initLocal0;
Person
<>
g__initLocal1;
Person
<>
g__initLocal2;
Person[] CS$
0
$
0000
;
IEnumerator CS$
5
$
0001
;
bool
CS$
4
$
0002
;
IDisposable CS$
0
$
0003
;
CS$
0
$
0000
=
new
Person[
3
];
<>
g__initLocal0
=
new
Person();
<>
g__initLocal0.Name
=
"
叶华斌
"
;
<>
g__initLocal0.Age
=
0x17
;
CS$
0
$
0000
[
0
]
=
<>
g__initLocal0;
<>
g__initLocal1
=
new
Person();
<>
g__initLocal1.Name
=
"
王昌文
"
;
<>
g__initLocal1.Age
=
0x16
;
CS$
0
$
0000
[
1
]
=
<>
g__initLocal1;
<>
g__initLocal2
=
new
Person();
<>
g__initLocal2.Name
=
"
吴朝剑
"
;
<>
g__initLocal2.Age
=
0x15
;
CS$
0
$
0000
[
2
]
=
<>
g__initLocal2;
array
=
CS$
0
$
0000
;
pa
=
new
PersonArray(array);
//
以上做些变量声明的工作
CS$
5
$
0001
=
pa.GetEnumerator();
//
①先调用IEnumerable中得方法返回一个迭代器IEnumerator也就是PersonEnumerator
Label_0084:
try
{
goto
Label_00B6;
//
②使用goto语句跳到下面Label_00B6:处
Label_0086:
item
=
(Person) CS$
5
$
0001
.Current;
//
④存在就通过Current属性返回当前值
Console.WriteLine(
string
.Format(
"
姓名:{0},年龄{1}
"
, item.Name, (
int
) item.Age));
Label_00B6:
if
(CS$
5
$
0001
.MoveNext()
!=
null
)
//
③移动到下一个元素判断是否存在
{
goto
Label_0086;
}
goto
Label_00E2;
//
⑤不存在就跳出foreach语句
}
finally
{
Label_00C5:
CS$
0
$
0003
=
CS$
5
$
0001
as
IDisposable;
if
((CS$
0
$
0003
==
null
)
!=
null
)
{
goto
Label_00E1;
}
CS$
0
$
0003
.Dispose();
Label_00E1:;
}
Label_00E2:
return
;
大概执行顺序是这样的:
1. 执行foreach语句前先调用IEnumerable.GetEnumorator()返回一个IEnumerator类型的枚举器。
2. 调用IEnumerator.MoveNext()从前一个元素的位置移动到下一个
3. 判断是否是最后一个元素,是跳出循环,否往下执行
4. 调用IEnumerator.Current属性返回当前的元素
5. 执行foreach语句块内的内容
6. 跳至第2步
好了到这一步我们需要的功能也都已经完成了,但总感觉一路走来有点艰辛呀,为什么呢,因为这个PersonEnumerator类让我们的实现变得复杂了许多,接下来再来看一种简洁的形式:
namespace
Demo
{
class
Program
{
static
void
Main(
string
[] args)
{
Person[] array
=
{
new
Person { Name
=
"
叶华斌
"
, Age
=
23
},
new
Person { Name
=
"
王昌文
"
, Age
=
22
},
new
Person { Name
=
"
吴朝剑
"
, Age
=
21
}};
PersonArray pa
=
new
PersonArray(array);
foreach
(Person item
in
pa)
{
Console.WriteLine(
string
.Format(
"
姓名:{0},年龄{1}
"
, item.Name, item.Age));
}
}
}
public
class
PersonArray : IEnumerable
{
Person[] array;
public
PersonArray(Person[] array)
{
this
.array
=
array;
}
public
IEnumerator GetEnumerator()
{
for
(
int
i
=
0
; i
<
array.Length; i
++
)
{
yield
return
array[i];
}
}
}
public
class
Person
{
public
string
Name {
get
;
set
; }
public
int
Age {
get
;
set
; }
}
}
通过yield return关键字就可以省去编写PersonEnumerator,通过反编译工具看到PersonArray实现代码:
public
class
PersonArray : IEnumerable
{
//
Fields
private
Person[] array;
//
Methods
public
PersonArray(Person[] array);
public
IEnumerator GetEnumerator();
//
Nested Types
[CompilerGenerated]
private
sealed
class
<
GetEnumerator
>
d__0 : IEnumerator
<
object
>
, IEnumerator, IDisposable
{
//
Fields
private
int
<>
1__state;
private
object
<>
2__current;
public
PersonArray
<>
4__this;
public
int
<
i
>
5__1;
//
Methods
[DebuggerHidden]
public
<
GetEnumerator
>
d__0(
int
<>
1__state);
private
bool
MoveNext();
[DebuggerHidden]
void
IEnumerator.Reset();
void
IDisposable.Dispose();
//
Properties
object
IEnumerator
<
object
>
.Current { [DebuggerHidden]
get
; }
object
IEnumerator.Current { [DebuggerHidden]
get
; }
}
}
原来编译器通过内嵌类来帮我们实现了PersonEnumerator,只是名字不同叫做<GetEnumerator>d__0罢啦。
最后再来一个泛型版本的:
namespace
Demo
{
class
Program
{
static
void
Main(
string
[] args)
{
Person[] array
=
{
new
Person { Name
=
"
叶华斌
"
, Age
=
23
},
new
Person { Name
=
"
王昌文
"
, Age
=
22
},
new
Person { Name
=
"
吴朝剑
"
, Age
=
21
}};
MyList
<
Person
>
pa
=
new
MyList
<
Person
>
(array);
foreach
(Person item
in
pa)
{
Console.WriteLine(
string
.Format(
"
姓名:{0},年龄{1}
"
, item.Name, item.Age));
}
}
}
public
class
MyList
<
T
>
:IEnumerable
<
T
>
{
T[] array;
public
MyList(T[] array)
{
this
.array
=
array;
}
public
IEnumerator
<
T
>
GetEnumerator()
{
return
new
MyListEnumerator
<
T
>
(array);
}
IEnumerator IEnumerable.GetEnumerator()
{
throw
new
NotImplementedException();
}
}
public
class
MyListEnumerator
<
T
>
: IEnumerator
<
T
>
{
T[] array;
int
position;
public
MyListEnumerator(T[] array)
{
this
.array
=
array;
this
.position
=
-
1
;
}
object
IEnumerator.Current
{
get
{
if
(
-
1
<
position
&&
position
<
array.Length)
{
return
array[position];
}
else
{
throw
new
IndexOutOfRangeException();
}
}
}
public
bool
MoveNext()
{
position
++
;
return
position
<
array.Length;
}
public
void
Reset()
{
position
=
-
1
;
}
T IEnumerator
<
T
>
.Current
{
get
{
if
(
-
1
<
position
&&
position
<
array.Length)
{
return
array[position];
}
else
{
throw
new
IndexOutOfRangeException();
}
}
}
public
void
Dispose()
{
}
}
public
class
Person
{
public
string
Name {
get
;
set
; }
public
int
Age {
get
;
set
; }
}
}
从上面的代码我们也可以对类库中得List、List<T>类有个大概的了解了,上面的MyList<T>的类相当于一个简略版本List<T>,它仅仅提供对列表的遍历,没有实现对元素操作罢了。