2010-03-25 8 views
10

Pourriez-vous me donner un exemple d'exécution différée avec une évaluation avide en C#?Exécution différée et évaluation avide

I lu à partir de MSDN que l'exécution différée dans LINQ peut être mis en oeuvre soit avec évaluation paresseuse ou avide. Je pourrais trouver des exemples dans l'Internet pour l'exécution différée avec l'évaluation paresseuse, cependant je ne pourrais trouver aucun exemple pour l'exécution différée avec l'évaluation avide.

De plus, comment l'exécution différée diffère-t-elle de l'évaluation paresseuse? À mon point de vue, les deux ont la même apparence. Pourriez-vous s'il vous plaît fournir un exemple pour cela aussi?

Répondre

1

Une façon que vous pouvez évaluer avec impatience une exécution différée IEnumerable est de transformer simplement dans un tableau en utilisant la fonction .ToArray() de LINQ. Cela force l'évaluation de l'énumération complète, puis vous avez le tableau que vous pouvez faire ce que vous voulez.

+0

Ho Joel Martinez, merci beaucoup pour votre réponse. –

+0

Pas si vite! Cela ne fonctionnera pas pour les données relationnelles. –

+0

Qu'est-ce que les données relationnelles ont à faire avec l'évaluation avide d'un ienumerable? même si l'ienumerable est contre quelque chose comme linq2sql ou ef, il va itérer tous les enregistrements en mémoire (ce qui vous permet de fermer la connexion db tôt). –

43

Bellow est ma réponse mais aussi noter que Jon Skeet en a parlé aujourd'hui sur son blog et sur le fait qu'il ne soit pas totalement d'accord avec le sens MSDN de "Lazy" car MSDN n'est pas vraiment clair de ce paresseux exactement dire quand ils l'utilisent dans Just how lazy are you ? son poste faire pour une lecture intéressante. En outre WikipediaWikipedia en outre supposer que trois règles doivent être conservées pour évaluation paresseux et troisième point n'est pas respectée dans MSDN signifiant que l'expression sera évaluée plusieurs fois si GetEnumerator est appelée à nouveau (par la spécification Reset n'est pas implémentée sur énumérateur objets générés à l'aide du mot clé yield et la plupart des LINQ utiliser actuellement)


Considérant une fonction

int Computation(int index) 

exécution immédiate

IEnumerable<int> GetComputation(int maxIndex) 
{ 
    var result = new int[maxIndex]; 
    for(int i = 0; i < maxIndex; i++) 
    { 
     result[i] = Computation(i); 
    } 
    return result; 
} 
  • Lorsque la fonction est appelée Computation est exécutée maxIndex fois
  • GetEnumerator retourne une nouvelle instance de ne rien faire recenseur plus.
  • Chaque appel à MoveNext met la valeur stockée dans la cellule Array suivante dans le membre Current du IEnumerator et c'est tout.

Coût: Big avance, petit lors de l'énumération (seulement une copie)

différée mais l'exécution désireux

IEnumerable<int> GetComputation(int maxIndex) 
{ 
    var result = new int[maxIndex]; 
    for(int i = 0; i < maxIndex; i++) 
    { 
     result[i] = Computation(i); 
    } 
    foreach(var value in result) 
    { 
     yield return value; 
    } 
} 
  • Lorsque la fonction est appelée une instance d'une classe générée automatique (appelé "objet énumérable" dans la spécification) IEnumerable est créé et une copie de l'argument (maxIndex) y est stockée.
  • GetEnumerator renvoie une nouvelle instance de l'énumérateur qui ne fait rien de plus.
  • Le premier appel à MoveNext exécute maxIndex fois la méthode de calcul, stocker le résultat dans un tableau et Current retournera la première valeur.
  • Chaque appel ultérieur au MoveNext mettra une valeur Current stockée dans le tableau.

Coût: rien d'avance, Big quand le début de l'énumération, Petit cours de l'énumération (seulement une copie)

reportés et exécution paresseux

IEnumerable<int> GetComputation(int maxIndex) 
{ 
    for(int i = 0; i < maxIndex; i++) 
    { 
     yield return Computation(i); 
    } 
} 
  • Lorsque la fonction est appelée même chose que le cas d'exécution paresseux se produit.
  • GetEnumerator renvoie une nouvelle instance de l'énumérateur ne faisant rien de plus.
  • Chaque appel à MoveNext exécute une fois le code Computation, placez la valeur dans Current et laissez l'appelant agir immédiatement sur le résultat.

La plupart des linq utilisent une exécution différée et paresseuse, mais certaines fonctions ne peuvent pas être similaires au tri.

Coût: rien d'avance, modéré pendant l'énumération (le calcul est exécuté là)

Pour résumer

  • immédiate signifie que le calcul/l'exécution se fait dans la fonction et a terminé une fois la fonction revenir. (Entièrement eager évaluation comme la plupart du code C# ne)
  • différés/Eager signifie que la plupart des travaux seront effectués sur la première MoveNext ou lorsque l'instance IEnumerator est créé (Pour IEnumerable il est quand GetEnumerator est appelé)
  • différés/Lazy signifie que le travail sera effectué chaque fois que MoveNext est appelé mais rien avant.

Parallel LINQ-t-il un peu différemment que le calcul pourrait être considéré comme différé/Lazy du point de vue de l'appelant, mais en interne le calcul d'un certain nombre d'éléments commencent en parallèle dès que l'énumération commence. Le résultat est que si la prochaine valeur est déjà là, vous l'obtenez immédiatement, mais sinon vous devrez l'attendre.

+0

VirtualBlackFox -> merci beaucoup ... c'est tan excellente explication que je n'ai jamais vu (au moins sur ce sujet :)) ..... mais je ne suis pas encore clair avec votre exemple pour "différé mais désireux évaluation "..., quand nous utilisons le mot clé de retour, il devient paresseux ... alors comment dites-vous cela comme" évaluation avide ".. pourriez-vous s'il vous plaît expliquer cela? En outre, comment l'exécution différée diffère-t-elle de l'évaluation paresseuse?l'exécution différée et l'évaluation paresseuse sont-elles identiques? –

+0

La description que j'ai ajoutée sous chaque exemple montre la différence: dans ce cas différé signifie que l'exécution sera faite lors de l'énumération non lors de l'appel de la fonction. Quand désireux même si cela arrive, tout le calcul sera fait en même temps (quand GetNext est appelé pour la première fois) alors que l'évaluation paresseuse implique que chaque appel GetNext fasse partie du travail. –

+4

Le meilleur exemple pour la différence est "Select" et "Reverse": les deux sont différés, la source énumérable est simplement stockée par l'appel à la méthode. Select est paresseux car chaque fois que GetNext est appelé sur le résultat, GetNext est appelé sur la source, il fonctionne même avec des sources infinies et si un calcul complexe est effectué par la source, la charge sera distribuée. Inverser dans l'autre main est désireux, il faut parcourir toute la source enumerable avant même de retourner son premier élément, si la source fait des calculs complexes, ils seront tous exécutés sur le premier appel GetNext sur le résultat. –

Questions connexes