C#之旅(二): IEnumerable IEnumerator

又是两个以rablerator结尾的两个接口,每次看到这种定义我就会很头疼,很容易搞混掉,所以这次我要将自己的学习记录下来,慢慢的把它们给啃掉,好记性不如烂笔头,也方便今后自己的复习。

通常在编程中我们常常面临这样一个问题就是对一个集合的遍历通常也就是一个数组或是一个链表,比如说一个保存了整个班级名单的变量还是一个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)来点名,记录委员就不停的MoveNextMoveNext点到的同学呢(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 ; }
    }
}

 

 

输出结果:

 C#之旅(二): IEnumerable IEnumerator

值得一看的是反编译之后它的具体实现如何,来看下:

 

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 ; }
    }
}

 

  

从上面的代码我们也可以对类库中得ListList<T>类有个大概的了解了,上面的MyList<T>的类相当于一个简略版本List<T>,它仅仅提供对列表的遍历,没有实现对元素操作罢了。

 

你可能感兴趣的:(enum)