2010-09-17 5 views
9

Actuellement, je travaille avec certaines bibliothèques appliquant une exécution différée via des itérateurs. Dans certaines situations, j'ai besoin de "transmettre" l'itérateur reçu simplement. C'est à dire. Je dois obtenir l'instance IEnumerable<T> de la méthode appelée et la renvoyer immédiatement.Quelle est la différence exacte entre renvoyer une instance IEnumerable et l'instruction return return dans C#

Maintenant ma question: Y a-t-il une différence significative entre simplement renvoyer le IEnumerable<T> reçu ou le re-céder par une boucle?

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

Quel est l'avantage de donner de nouveaux rendements? Si vous voulez effectuer une action sur les éléments, vous ne serez pas en boucle, non? Si non, pourquoi ne pas utiliser la version short-code? –

+0

Je conclus de mes expériences (et de l'inspection IL), que l'exécution de GetByReYielding() sera elle-même différée (indépendamment du fonctionnement de GetIterator()), mais pas l'exécution de GetByReturn(). C'est tout. – Nico

Répondre

1

Il n'y a pas de différence significative (en dehors de peut-être performance) entre les deux puisque vous ne faites rien avec l'énumérateur de GetIterator(). Cela n'aurait de sens que de re-céder si vous alliez faire quelque chose avec l'énumérateur, comme le filtrer.

1

Je ne vois pas de différence significative autre que le ballonnement de code.

3

Ils sont différents. Par exemple, si le GetIterator() a déclaré que:

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

Si vous ne le faites pas re-céder, la GetIterator() et la boucle se est immédiatement exécutée. Par conséquent, la réponse dépend de la façon dont vous implémentez GetIterator(). S'il est certain que GetIterator() cédera, il n'y a aucun intérêt à le relancer.

+2

Même s'il utilise un itérateur, il parcourra toute la liste avant le premier _yield_. –

0

Il existe une différence significative.

L'exécution de GetByReYielding() sera exécutée différemment (car il s'agit d'un bloc d'itérateur). Si vous avez utilisé un paramètre dans GetByReturn() ou GetByReYielding() et vérifié ce paramètre à l'exécution pour la nullité (ou toute autre validation), cette vérification serait effectuée immédiatement lorsque GetByReturn() est appelée mais pas immédiatement lorsque GetByReYielding() est appelée ! La validation dans GetByReYielding() serait effectuée de manière différée, lorsque le résultat est itéré. - C'est souvent, bien, "trop ​​tard". Voir ici:

// 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; 
    } 
} 

M. Skeet a expliqué ce concept dans http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx. Les opérateurs de requête standard fournis dans .Net utilisent des fonctions wrapper non différées (par exemple Where()) qui vérifient les paramètres et appellent ensuite la fonction itérateur de base (comme je l'ai montré dans mon implémentation de GetByReturn()).

J'espère que cela aide.

Questions connexes