2010-12-01 4 views
2

j'ai écrit un programme simple, voici à quoi il ressemble, certains détails cachés:Linq exécution différée

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 

namespace routeaccounts 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      //Draw lines from source file 
      var lines = File.ReadAllLines("accounts.txt").Select(p => p.Split('\t')); 
      //Convert lines into accounts 
      var accounts = lines.Select(p => new Account(p[0], p[1], p[2], p[3])); 
      //Submit accounts to router 
      var results = accounts.Select(p => RouteAccount(p)); 
      //Write results list to target file 
      WriteResults("results.txt", results); 
     } 

     private static void WriteResults(string filename, IEnumerable<Result> results) 
     { 
      ... disk write call ... 
     } 

     private static Result RouteAccount(Account account) 
     { 
      ... service call ... 
     } 
    } 
} 

Ma question est - évidemment, lors de la sélection d'un contexte de données, l'exécution est reportée. Si vous remarquez, dans la première déclaration de la fonction 'Main', j'interroge à partir de File.ReadAllLines ("accounts.txt"). Est-ce un mauvais choix? Si j'énumère le résultat final, cette déclaration sera-t-elle répétée?

Je peux simplement .ToArray() ou saisir les résultats à l'avance, si je sais que c'est un problème, mais je suis curieux de savoir ce qui se passe dans les coulisses.

Répondre

3

Il ne va pas lire le fichier à plusieurs reprises, pas - parce que partie de l'exécution n'est pas différé. Il retournera un tableau, puis l'appel à Select vous renverra une séquence ... la projection sera différée, mais pas la lecture du fichier. Ce tableau restera en mémoire jusqu'à ce que tout ce qui s'y rapporte (directement ou indirectement) soit éligible pour la récupération de place ... il n'aura pas besoin de relire le fichier.

D'autre part, vous peut vouloir lire les résultats en utilisant ToList() ou quelque chose de similaire de toute façon - car de cette manière, vous obtenez de trouver des erreurs avant commencer à écrire les résultats. C'est souvent une bonne idée de s'assurer que vous avez toutes les données dont vous avez besoin avant de commencer à exécuter du code avec des effets secondaires (ce que j'imagine WriteResults). Évidemment, c'est moins efficace en termes de quantité de données nécessaires en mémoire à la fois ... c'est un équilibre que vous devrez peser vous-même.

+2

c'est un bon conseil, j'ai eu des problèmes avec LINQ où l'exécution différée a effectivement causé des exceptions à être lancées dans des endroits que je ne m'attendais pas à devoir gérer. – Sprague

4

Mieux vaut utiliser File.ReadLines dans .NET 4.0 pour obtenir une lecture paresseuse du fichier. Comme c'est le cas maintenant, la lecture du fichier n'est pas différée et va lire le fichier entier en mémoire quand File.ReadAllLines retourne. Cela n'arrivera qu'une fois.

+0

Merci pour votre réponse, mais pouvez-vous me dire comment il est décidé de reporter ou non quelque chose? Ce serait des informations utiles – Sprague

+1

@Eugarps: Il n'y a pas de règles. Vous pouvez dire que 'File.ReadAllLines' ne diffère pas parce qu'il retourne un tableau. Mais juste parce qu'une méthode renvoie 'IEnumerable ' ne signifie pas qu'elle est différée (l'objet concret qu'elle renvoie pourrait être, disons, une 'Liste '). Le fait qu'une méthode retourne 'IEnumerable ' est un indice qu'il pourrait être différé mais pas une garantie. De plus, il y a une énorme différence entre l'exécution différée et l'exécution paresseuse (pensez 'Enumerable.OrderBy' qui est différé mais pas paresseux). – jason

+1

J'étais divisé entre ta réponse et celle de Jon, mais je suis en train de voter parce qu'il s'est produit en premier. Particulièrement utile dans votre réponse était la référence à ReadLines, que je n'ignorais pas mais convient très bien à mon but. Votez pour celui-là. – Sprague

0
 //File is read now, but split later. 
     var lines = File.ReadAllLines("accounts.txt").Select(p => p.Split('\t')); 
     //Accounts are new'd up later. 
     var accounts = lines.Select(p => new Account(p[0], p[1], p[2], p[3])); 
     //Accounts are Routed later. 
     var results = accounts.Select(p => RouteAccount(p)); 
     //Write results list to target file 
     WriteResults("results.txt", results); 

    private static void WriteResults(string filename, IEnumerable<Result> results) 
    { 
     //file is split, accounts are new'd up and routed by enumerating results 
     List<Result> items = results.ToList(); 
    } 
Questions connexes