2010-04-16 17 views
5

Sto avendo un po 'di problemi con un metodo in cui io uso yield return questo non funziona ...Metodo non chiamata quando si usa dei rendimenti

public IEnumerable<MyClass> SomeMethod(int aParam) 
{ 
    foreach(DataRow row in GetClassesFromDB(aParam).Rows) 
    { 
     yield return new MyClass((int)row["Id"], (string)row["SomeString"]); 
    }  
} 

Il codice di cui sopra non si esaurisce mai, quando la chiamata è fatto a questo metodo, lo scavalca.

Tuttavia se cambio di ...

public IEnumerable<MyClass> SomeMethod(int aParam) 
{ 
    IList<MyClass> classes = new List<MyClass>(); 

    foreach(DataRow row in GetClassesFromDB(aParam).Rows) 
    { 
     classes.Add(new MyClass((int)rows["Id"], (string)row["SomeString"]); 
    } 

    return classes; 
} 

funziona bene.

Non capisco perché il primo metodo non funzioni mai, potresti aiutarmi a capire cosa sta succedendo qui?

+0

Come si chiama il metodo? – gammelgul

+0

Chiamando in un costruttore come questo: 'Prop = SomeMethod (param);' – DaveParsons

risposta

7

La versione "rendimento" è solo "eseguita" quando il chiamante inizia effettivamente a enumerare la raccolta restituita.

Se, per esempio, si ottiene solo la raccolta:

var results = SomeObject.SomeMethod (5); 

e non fare qualsiasi cosa con esso, il SomeMethod non verrà eseguito.

Solo quando si inizia a elencare la raccolta results, verrà colpito.

foreach (MyClass c in results) 
{ 
    /* Now it strikes */ 
} 
+0

Brillante, esattamente quello che dovevo sapere! Grazie – DaveParsons

2

yield return metodi sono effettivamente convertiti in classi macchina a stati che recuperano informazioni pigramente - solo quando effettivamente chiedete. Ciò significa che per poter effettivamente estrarre dati, devi eseguire un'iterazione sul risultato del tuo metodo.

// Gives you an iterator object that hasn't done anything yet 
IEnumerable<MyClass> list = SomeMethod(); 

// Enumerate over the object 
foreach (var item in list) { 
    // Only here will the data be retrieved. 
    // The method will stop on yield return every time the foreach loops. 
} 

Il motivo viene eseguito nel secondo caso è perché non c'è alcun blocco rendimento, e quindi l'intero metodo viene eseguito in una volta.

In questo caso specifico, è improbabile che tu abbia alcun vantaggio di utilizzare un blocco iteratore su uno normale perché il tuo GetClassesFromDb() non è uno dei due. Ciò significa che recupererà tutti i dati contemporaneamente alla prima esecuzione. I blocchi Iterator si usano al meglio quando puoi accedere agli elementi uno alla volta, perché in questo modo puoi fermarti se non ne hai più bisogno.

0

Ho dovuto apprendere in modo quasi disastroso quanto è bello/pericoloso yield quando ho deciso di fare in modo che il parser della nostra azienda legga pigramente i dati in arrivo. Fortunatamente solo una delle poche funzioni implementative ha effettivamente utilizzato la parola chiave yield. Ci sono voluti alcuni giorni per rendersi conto che in pratica non stava facendo alcun lavoro.

La parola chiave di rendimento sarà come pigri come il possibile, compreso saltando il metodo del tutto, se non si mette a lavorare con qualcosa come .ToList() o .FirstOrDefault() o .Any()

Qui di seguito sono due varianti, una usando la parola chiave e una che restituisce una lista di diritti. Uno non si preoccuperà nemmeno di eseguire, mentre l'altro lo farà, anche se sembra lo stesso.

public class WhatDoesYieldDo 
{ 
    public List<string> YieldTestResults; 

    public List<string> ListTestResults; 

    [TestMethod] 
    public void TestMethod1() 
    { 
     ListTest(); 
     Assert.IsTrue(ListTestResults.Any()); 

     YieldTest(); 
     Assert.IsTrue(YieldTestResults.Any()); 
    } 

    public IEnumerable<string> YieldTest() 
    { 
     YieldTestResults = new List<string>(); 
     for (var i = 0; i < 10; i++) 
     { 
      YieldTestResults.Add(i.ToString(CultureInfo.InvariantCulture)); 
      yield return i.ToString(CultureInfo.InvariantCulture); 
     } 
    } 

    public IEnumerable<string> ListTest() 
    { 
     ListTestResults = new List<string>(); 

     for (var i = 0; i < 10; i++) 
     { 
      ListTestResults.Add(i.ToString(CultureInfo.InvariantCulture)); 
     } 

     return ListTestResults; 
    } 
} 

Morale della favola: Assicurarsi che se avere un metodo che restituisce IEnumerable e si utilizza yield in questo metodo, è avere qualcosa che iterare i risultati, o il metodo non verrà eseguito affatto.

Problemi correlati