2010-09-17 3 views
9

Momentan arbeite ich mit einigen Bibliotheken, die verzögerte Ausführung über Iteratoren anwenden. In manchen Situationen muss ich den empfangenen Iterator einfach "weiterleiten". I.e. Ich muss die IEnumerable<T> Instanz von der aufgerufenen Methode abrufen und sie sofort zurückgeben.Was ist der genaue Unterschied zwischen der Rückgabe einer IEnumerable-Instanz und der Yield Return-Anweisung in C#

Jetzt meine Frage: Gibt es einen relevanten Unterschied zwischen einfach die empfangene IEnumerable<T> zurückgeben oder es über eine Schleife wieder abgeben?

IEnumerable<int> GetByReturn() 
{ 
    return GetIterator(); // GetIterator() returns IEnumerable<int> 
} 
// or: 
IEnumerable<int> GetByReYielding() 
{ 
    for(var item in GetIterator()) // GetIterator() returns IEnumerable<int> 
    { 
     yield return item; 
    } 
} 
+2

Was ist der Vorteil der Nachergiebigkeit? Wenn Sie etwas an den Gegenständen ausführen wollen, werden Sie nicht herumlaufen, richtig? Wenn nicht, warum nicht die kürzere Version verwenden? –

+0

Ich folge aus meinen Experimenten (und IL-Inspektionen), dass die Ausführung von GetByReYielding() selbst verzögert wird (unabhängig davon, wie GetIterator() funktioniert), aber die Ausführung von GetByReturn() nicht. Das ist alles. – Nico

Antwort

1

Es gibt keinen relevanten Unterschied (abgesehen von vielleicht Leistung) zwischen den beiden, da Sie nichts mit der Enumerator von GetIterator() tun. Es würde nur Sinn ergeben, wenn du etwas mit dem Enumerator machen würdest, wie zum Beispiel Filter.

1

Ich sehe dort keinen relevanten Unterschied anders als Code Blähung.

3

Sie sind anders. Wenn zum Beispiel der GetIterator() erklärt wie:

IEnumerable<int> GetIterator() { 
    List<int> result = new List<int>(); 
    for(int i = 0; i < 1000; i++) { 
     result.Add(i); 
    } 
    return result; 
} 

Wenn Sie nicht wieder was tun, die GetIterator() und die Schleife wurde sofort ausgeführt. Daher hängt die Antwort davon ab, wie Sie GetIterator() implementieren. Wenn es sicher ist, dass GetIterator() nachgeben wird, so gibt es keinen Sinn, es nachzugeben.

+2

Auch wenn er einen Iterator verwendet, durchläuft er immer noch die gesamte Liste vor dem ersten _yield_. –

0

Es gibt einen relevanten Unterschied.

Die Ausführung von GetByReYielding() wird verzögert ausgeführt (da es sich um einen Iteratorblock handelt). Wenn Sie einen Parameter in GetByReturn() oder GetByReYielding() verwendet haben und diesen Parameter zur Laufzeit auf Nichtigkeit geprüft haben (oder eine andere Validierung durchgeführt haben), wird diese Prüfung sofort ausgeführt, wenn GetByReturn() aufgerufen wird, aber nicht sofort, wenn GetByReYielding() aufgerufen wird ! Die Validierung in GetByReYielding() würde verzögert durchgeführt werden, wenn das Ergebnis iteriert wird. - Das ist oft, naja, "zu spät". Siehe hier:

// Checks parameters early. - Fine. The passed argument will be checked directly when 
// GetByReturn() is called. 
IEnumerable<int> GetByReturn(IEnumerable<int> sequence) 
{ 
    if(null == sequence) 
    { 
     throw new ArgumentNullException("sequence"); 
    } 

    return GetIterator(); 
} 
// Checks parameters in a deferred manner. - Possibly not desired, it's "too" late. I.e.     // when the  
// result is iterated somewhere in a completely different location in your code the 
// argument passed once will be checked. 
IEnumerable<int> GetByReYielding(IEnumerable<int> sequence) 
{ 
    if(null == sequence) 
    { 
     throw new ArgumentNullException("sequence"); 
    } 

    for(var item in GetIterator()) 
    { 
     yield return item; 
    } 
} 

Herr Skeet dieses Konzept in http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx erklärt. Die Standardabfrageoperatoren, die in .Net bereitgestellt werden, verwenden nicht verzögerte Wrapperfunktionen (z. B. Where()), die Parameter prüfen und dann die Kerniteratorfunktion aufrufen (wie ich in meiner Implementierung von GetByReturn() gezeigt habe).

Ich hoffe, das hilft.

Verwandte Themen