熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

foreach和yield

2022-06-13   來源: .NET編程 

  foreach

  foreach無需要知道集合中元素個數就可以迭代集合中的元素它其實是迭代器模式的一個包裝就語言層面來說是while的另一種形式

  using System;

  using SystemCollections;

  public class People : IEnumerable//版本{    string[] names;    public People(string[] names)    {        thisnames = names;    }    public IEnumerator GetEnumerator()    {        return new Enumerator(thisnames);    }    private class Enumerator : IEnumerator    {        private int state = ;        string[] names;        internal Enumerator(string[] names)        {            thisnames = names;        }        public bool MoveNext()        {            state++;            return (state < namesLength);        }        public void Reset()        {            state = ;        }        public object Current        {            get            {                try                {                    return names[state];                }                catch (IndexOutOfRangeException)                {                    throw new InvalidOperationException();                }            }        }    }}public class Test{    public static void Main()    {        People people = new People(new string[]{aaabbbcccddd});        foreach (var person in people)        {            ConsoleWriteLine(person);        }        IEnumerator Enumerator = peopleGetEnumerator();        while (EnumeratorMoveNext())        {            ConsoleWriteLine(EnumeratorCurrent);        }    }}查看IL代碼先看foreach代碼塊的IL代碼

  IL_f:  callvirt   instance class [mscorlib]SystemCollectionsIEnumerator People::GetEnumerator()

  IL_:  stlocs    CS$$

  try

  {

  IL_:  brs       IL_

  IL_:  ldlocs    CS$$

  IL_a:  callvirt   instance object [mscorlib]SystemCollectionsIEnumerator::get_Current()

  IL_f:  stloc

  IL_:  ldloc

  IL_:  call       void [mscorlib]SystemConsole::WriteLine(object)

  IL_:  ldlocs    CS$$

  IL_:  callvirt   instance bool [mscorlib]SystemCollectionsIEnumerator::MoveNext()

  IL_d:  brtrues   IL_

  IL_f:  leaves    IL_

  }  // end try

  finally

  {

  IL_:  ldlocs    CS$$

  IL_:  isinst     [mscorlib]SystemIDisposable

  IL_:  stlocs    CS$$

  IL_a:  ldlocs    CS$$

  IL_c:  brfalses  IL_

  IL_e:  ldlocs    CS$$

  IL_:  callvirt   instance void [mscorlib]SystemIDisposable::Dispose()

  IL_:  endfinally

  }  // end handler再看while代碼塊的IL代碼

  IL_:  callvirt   instance class [mscorlib]SystemCollectionsIEnumerator People::GetEnumerator()

  IL_:  stloc

  IL_:  brs       IL_

  IL_:  ldloc

  IL_a:  callvirt   instance object [mscorlib]SystemCollectionsIEnumerator::get_Current()

  IL_f:  call       void [mscorlib]SystemConsole::WriteLine(object)

  IL_:  ldloc

  IL_:  callvirt   instance bool [mscorlib]SystemCollectionsIEnumerator::MoveNext()

  IL_a:  brtrues   IL_可以看出foreach語句塊IL代碼與while語句塊的IL代碼相比只多了finally部分的資源處理但循環部分的代碼基本沒有任何分別

  如果讓private class Enumerator 增加IDisposable接口將while語句塊改寫為如下

  using((IDisposable)Enumerator)

  {

  while (EnumeratorMoveNext())

  {

  ConsoleWriteLine(EnumeratorCurrent);

  }

  }得到的IL代碼將與foreach基本沒有區別

  由此可見foreach是編譯器給我們的一個語法糖它包裝了while

  yield

  在上面的代碼中枚舉器private class Enumerator 是自己實現了IEnumerator接口中的方法使用yield語句可以讓編譯器自動實現省去了自己寫的麻煩(包含yield語句的方法或屬性必須聲明為返回IEnumerable 或IEnumerator )所以上面的public class People 可以這樣寫

  public class People : IEnumerable//版本

  {

  string[] names;

  public People(string[] names)

  {

  thisnames = names;

  }

  public IEnumerator GetEnumerator()

  {

  for (int i = ; i < namesLength; i++)

  {

  yield return names[i];

  }

  }

  }

  反編譯之後看IL代碼 

  這是版本people類

  {F)2KABVS%RLZ51ODZ3F%LW

  這是版本的people類

  U1PY(JNW_M%W@I~~953UI@Q[1]

  自己寫的迭代器類Enumerator和編譯器生成的迭代器類<GetEnumerator>d_相比較<GetEnumerator>d_增加了IDisposable接口另外就是增加了IEnumerator接口的泛型版本的繼承如果願意Enumerator也可以實現這兩個接口

  用yield還可以返回IEnumerable接口類型

  using System;

  using SystemCollections;

  public class People

  {

  string[] names;

  public People(string[] names)

  {

  thisnames = names;

  }

  public IEnumerable PeopleEven()

  {

  for (int i = ; i < namesLength; i += )

  {

  yield return names[i];

  }

  }

  }

  public class Test

  {

  public static void Main()

  {

  People people = new People(new string[] { aaa bbb ccc ddd });

  foreach (var person in peoplePeopleEven())

  {

  ConsoleWriteLine(person);

  }

  }

  }

  該PeopleEven()返回的IEnumerable類<PeopleEven>d_會自動實現GetEnumerator()方法和IEnumerator接口中的方法看IL代碼

  1_thumb4

  PeopleEven()返回的類<PeopleEven>d_實現了IEnumerable和IEnumerator接口以及它們的泛型版本還實現了IDisposable接口因此可以將這個接口都實現將類改寫

  using System;

  using SystemCollections;

  public class People : IEnumerable IEnumerator IDisposable

  {

  string[] names = { bbbb dddd };

  int state = ;

  public IEnumerator GetEnumerator()

  {

  return new People();

  }

  public bool MoveNext()

  {

  state++;

  return (state < namesLength);

  }

  public void Reset()

  {

  state = ;

  }

  public object Current

  {

  get

  {

  try

  {

  return names[state];

  }

  catch (IndexOutOfRangeException)

  {

  throw new InvalidOperationException();

  }

  }

  }

  public void Dispose()

  {

  }

  }

  public class Test

  {

  public static void Main()

  {

  People people = new People();

  foreach (var person in people)

  {

  ConsoleWriteLine(person);

  }

  }

  }

  看IL代碼

  24_thumb6

  類People與PeopleEven()返回的類<PeopleEven>d_基本相同只是少了兩個接口的泛型版本的實現

  由此可見yield是一個更大的語法糖它可以自動實現IEnumerable和IEnumberator接口中的方法自動替我們實現迭代器

  用foreach和yield可以非常方便地使用迭代模式


From:http://tw.wingwit.com/Article/program/net/201311/11643.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.