.NET学习之Foreach……

  对于.NET Framework中的foreach使用“算得上”是很熟练了,但今天看了书才发现自己对其一点也不了解……

  对于.NET Framework提供的“标准”的集合类型可以直接使用foreach,如(ArrayArrayListHashTable),除此之外,对于自定义的集合对象,也可以使其支持foreach的使用,只要实现IEnumerable接口即可(刚提到的几种集合类型都实现了这个接口)。先看个例子(代码参考http://jangmon.blogbus.com/logs/36380490.html 

ExpandedBlockStart.gif 例一
public   class  HelloCollection : IEnumerable
    {
        
public  IEnumerator GetEnumerator ()
        {
            
yield   return   " Hello " ;
            
yield   return   " World " ;
        }
}
  
class  CustomerForeach
    {
        
static   void  Main()
        {
            HelloCollection helloCollection 
=   new  HelloCollection();
            
foreach  ( string  s  in  helloCollection)
            {
                Console.WriteLine(s 
+   " …… " );
            }
            Console.ReadKey();
        }
    }

 运行可以看到正确的输出了“Hello……  World……”,问题的关键就在 “IEnumerator”的IEnumerable.GetEnumerator()”这个方法,以及yield关键字,单步运行程序会发现程序运行至foreach语句之后跳转至HelloCollection中的GetEnumerator方法,然后遇到“yield  return”就返回,接着执行下一个,直到返回所有的yield……

修改上述代码,比方说改为如下代码,先猜测下结果,然后运行看输出什么。

 

ExpandedBlockStart.gif 例二
public   class  HelloCollection : IEnumerable
    {
        
public  IEnumerator GetEnumerator()
        {
            Console.WriteLine(
" 跳转至GetEnumerator方法 " );
            Console.WriteLine(
" 即将输出 " );
            
yield   return   " Hello " ;
            Console.WriteLine(
" 下一个输出 " );
            
yield   return   " World " ;
        }
    }
class  CustomerForeach
    {
        
static   void  Main()
        {
            HelloCollection helloCollection 
=   new  HelloCollection();
            
foreach  ( string  s  in  helloCollection)
            {
                Console.WriteLine(
" foreach方法 " );
                Console.WriteLine(s );
            }
            Console.ReadKey();
        }
    }

结果输出的是

跳转至GetEnumerator方法
即将输出:
foreach方法
Hello
下一个输出:
foreach方法
World

   看明白了?遇到一个yield return 就返回到foreach,而且在foreach内部使用的s就是返回的值……

 

再看个代码 

ExpandedBlockStart.gif 例三
class  CustomerForeach
    {
        
static   void  Main()
        {
            Peoples ps 
=   new  Peoples();
            
foreach  (People p  in  ps)
            {
                Console.WriteLine(p);
            }
            Console.ReadKey();
        }
    }

    
class  People
    {
        
public   string  Name;
        
public   int  Age;
        
// 重写基类object的Tostring方法()以按一下格式提供有意义的信息
        
// 姓名:年龄
         public   override   string  ToString()
        {
            
// return base.ToString();
             return  Name  +   " : "   +  Age.ToString();
        }
    }
    
class  Peoples:IEnumerable
    {
        
private  People[] objects;
        
private  Random ran  =   new  Random();
        
private   void  FillElements()
        {
            objects 
=   new  People[ 10 ];
            
for  ( int  i  =   0 ; i  <   10 ; i ++ )
            {
                objects[i] 
=   new  People();
                objects[i].Name 
=   " People "   +  (i + 1 ).ToString();
                objects[i].Age 
=  ran.Next( 1 , 100 );
            }
        }

        
public  Peoples()
        {
            FillElements();
        }

        
#region  IEnumerable 成员

        IEnumerator IEnumerable.GetEnumerator()
        {        
           
//  throw new NotImplementedException();
             foreach  (People people  in  objects)
            {
                 
yield   return  people;
            }  
        }

        
#endregion
    }

 

这个自己测试吧,通过查找可以知道IEnumerable里就一个方法是IEnumerator GetEnumerator();而IEnumerator接口原型如下(查询VS2008文档得知) 

public   interface  IEnumerator
{
// 获取集合中的当前元素。
   object  Current {  get ; }
// 将枚举数推进到集合的下一个元素。
bool  MoveNext();
// 将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
void  Reset();
}

  以下是自带的解释

ExpandedBlockStart.gif 解释
C# 语言的  foreach  语句(在 Visual Basic 中为 For Each)隐藏了枚举数的复杂性。因此,建议使用  foreach ,而不直接操作枚举数。

枚举数可用于读取集合中的数据,但不能用于修改基础集合。

最初,枚举数定位在集合中第一个元素前。Reset 方法还会将枚举数返回到此位置。在此位置上,Current 属性未定义。因此,在读取 Current 的值之前,必须调用 MoveNext 方法将枚举数提前到集合的第一个元素。

在调用 MoveNext 或 Reset 之前,Current 返回同一对象。MoveNext 将 Current 设置为下一个元素。

如果 MoveNext 越过集合的末尾,则枚举数将被放置在此集合中最后一个元素的后面,而且 MoveNext 返回 
false 。当枚举数位于此位置时,对 MoveNext 的后续调用也返回  false 。如果上一个 MoveNext 调用返回  false ,则 Current 未定义。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext。

只要集合保持不变,枚举数就保持有效。如果对集合进行更改(如添加、修改或删除元素),则枚举数将失效且不可恢复,而且其行为是不确定的。

枚举数没有对集合的独占访问权;因此,枚举通过集合在本质上不是一个线程安全的过程。若要确保枚举过程中的线程安全,可以在整个枚举过程中锁定集合。若要允许多个线程访问集合以进行读写操作,则必须实现自己的同步。

  最后在加个例子,跟上述相关的

ExpandedBlockStart.gif 例五:(IEnumerator)
class  CustomerForeach
    {
        
static   void  Main()
        {           
            
int [] values  =   new   int [ 10 ]{ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 };
            ArrayList arrl 
=   new  ArrayList();
            arrl.Add(
" Arr1 " );
            arrl.Add(
" Arr3 " );
            arrl.Add(
" Arr5 " );
            Hashtable hst 
=   new  Hashtable();
            hst.Add(
2 " hst2 " );
            hst.Add(
4 " hst4 " );
            hst.Add(
6 " hst6 " );
            PrintCollection(values);
            PrintCollection(arrl);
            PrintCollection(hst);
            Console.ReadKey();
        }

        
static   void  PrintCollection(IEnumerable obj)
        {
            
// 获取迭代访问器
            IEnumerator itor  =  obj.GetEnumerator();
            
// 只要集合中还有为访问的元素。movenext方法返回True
             while  (itor.MoveNext())
            {
                
// itor.Current返回当前的对象
                
// 本例仅简单的输出其Tostring()方法的结果
                Console.WriteLine(itor.Current.ToString());
            }
        }
    }

 看明白了吗?

参考书籍:金旭亮的《.NET 2.0 面向对象编程揭秘》

转载于:https://www.cnblogs.com/hsrzyn/archive/2009/12/16/1625987.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值