2010-03-15 17 views
18

Olası Çoğalt:
How does foreach work when looping through function results?C# - Bir foreach döngüsünün her yinelemesi için işlev çağrılıyor mu?

aşağıdaki gibi özelliğe sahip ise -() bir foreach döngü içinde her yineleme çağrısında alacak ReturnParts, ya da bir kez aradı alacak ?

private void PrintParts() 
{ 
    foreach(string part in ReturnParts()) 
    { 
     // Do Something or other. 
    } 
} 

private string[] ReturnParts() 
{ 
    // Build up and return an array. 
} 
+1

Yinelenen: http://stackoverflow.com/questions/1632810/how-does-foreach-work-when-looping-through-function-results –

cevap

25

Sadece bir kez çağrılacak.

P.S. Birden çok kez çağırmak biraz mantıklı olur. Her seferinde sonucun farklı olmasını beklerseniz, her seferinde tekrar ararsınız. Ve sürekli değişen bir set üzerinde nasıl durursun?

9

Sadece bir kez çağrılır.

The foreach loop is equivalent to the following code:

IEnumerable<string> enumerator = (collection).GetEnumerator(); 
try { 
    while (enumerator.MoveNext()) { 
     string part = (string)enumerator.Current; 

     // Do Something or other. 

    } 
} finally { 
    IDisposable disposable = enumerator as System.IDisposable; 
    if (disposable != null) disposable.Dispose(); 
} 
17

Bunu ise her yineleme için mutliple kez o zaman evet öyle vurur fonksiyonu üzerine "ReturnParts" Bir kesme noktası yerleştirerek bu kendini belirleyebilir.

+0

öneri için teşekkürler. Bu kendi sorularınızı öğrenmek için iyi bir yoldur –

6
ederken, foreach için arasındaki farkları merak içinde

ve birkaç hafta önce bu yüzden bu test kodu yukarı yazdım gidiniz. yöntemlerin tümü aynı IL içine derlemek olacak (diğer sonra foreach sürümüne değişken adı.) hata ayıklama modunda birkaç NOP ifadeleri farklı pozisyonlarda olacaktır. Eric'in yorumun @ Başına

static void @for<T>(IEnumerable<T> input) 
{ 
    T item; 
    using (var e = input.GetEnumerator()) 
     for (; e.MoveNext();) 
     { 
      item = e.Current; 
      Console.WriteLine(item); 
     } 
} 
static void @foreach<T>(IEnumerable<T> input) 
{ 
    foreach (var item in input) 
     Console.WriteLine(item); 
} 
static void @while<T>(IEnumerable<T> input) 
{ 
    T item; 
    using (var e = input.GetEnumerator()) 
     while (e.MoveNext()) 
     { 
      item = e.Current; 
      Console.WriteLine(item); 
     } 
} 
static void @goto<T>(IEnumerable<T> input) 
{ 
    T item; 
    using (var e = input.GetEnumerator()) 
    { 
     goto check; 
    top: 
     item = e.Current; 
     Console.WriteLine(item); 
    check: 
     if (e.MoveNext()) 
      goto top; 
    } 
} 
static void @gotoTry<T>(IEnumerable<T> input) 
{ 
    T item; 
    var e = input.GetEnumerator(); 
    try 
    { 
     goto check; 
    top: 
     item = e.Current; 
     Console.WriteLine(item); 
    check: 
     if (e.MoveNext()) 
      goto top; 
    } 
    finally 
    { 
     if (e != null) 
      e.Dispose(); 
    } 
} 

...

Ben jenerik diziler for, while, 'git' ve foreach genişlettik. Şimdi for each ifadesi, dizinin dizinleyicisini kullanmayı düşünüyor. Nesne dizileri ve dizgiler benzer yollarla genişletilir. Nesneler Console.WriteLine ve Strings için yöntem çağrısı sırasıyla char item ve string copy... ile T item ve T[] copy... yerini alacak önce gerçekleşen bir boks kaldıracaktır. Tek kullanımlık numaratörün artık kullanılmadığından kritik bölümün artık gerekmediğini unutmayın.

static void @for<T>(T[] input) 
{ 
    T item; 
    T[] copy = input; 
    for (int i = 0; i < copy.Length; i++) 
    { 
     item = copy[i]; 
     Console.WriteLine(item); 
    } 
} 
static void @foreach<T>(T[] input) 
{ 
    foreach (var item in input) 
     Console.WriteLine(item); 
} 
static void @while<T>(T[] input) 
{ 
    T item; 
    T[] copy = input; 
    int i = 0; 
    while (i < copy.Length) 
    { 
     item = copy[i]; 
     Console.WriteLine(item); 
     i++; 
    } 
} 
static void @goto<T>(T[] input) 
{ 
    T item; 
    T[] copy = input; 
    int i = 0; 
    goto check; 
top: 
    item = copy[i]; 
    Console.WriteLine(item); 
    i++; 
check: 
    if (i < copy.Length) 
     goto top; 
} 
+0

@gotoTry derlenmiş IL göstermeden Aklıma gelen en genişletilmiş versiyonudur. –

+2

Güzel bitti. Tabii ki bu sadece IEnumerable uygulamak şeyler için açılımıdır. foreach dizileri ve dizeleri için farklı kod üretir. –

+0

@Eric: geri besleme için teşekkürler. En genel versiyona gidecektim ama bunu daha sonra uzatabilirim. (Ben çoğunlukla en çok "kötülük" olarak düşünen goto versiyonuna ilgi duyuyordum.) –

İlgili konular