• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#之旅(二):IEnumerableIEnumerator

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

又是两个以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 { getset; }
        
public int Age { getset; }
    }
}

 

 

输出结果:

 

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

 

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 { getset; }
        
public int Age { getset; }
    }
}

 

 

通过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
    {
        

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#执行oraclesql语句出现中文不兼容的问题发布时间:2022-07-14
下一篇:
C#aspx页面动态加载ascx用户控件及利用反射调用其发布时间:2022-07-14
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap