2016-09-19 4 views
4

Je travaille sur une méthode pour trouver un chemin de fichier de configuration. Cela doit faire deux passes: d'abord trouver tous les fichiers de configuration existants, puis revenir en arrière et trouver le premier chemin accessible en écriture.Est-il possible de mettre en cache des résultats d'évaluation IEnumerable paresseux?

Bien qu'exhaustif pour ma situation particulière, cela m'a fait réfléchir: est-il possible d'avoir à la fois une évaluation paresseuse et d'éviter l'énumération multiple?

Pour illustrer ce que je veux dire, considérez le code suivant:

public IEnumerable<string> GetPaths() 
{ 
    Console.WriteLine("GetPaths() Returning 'one'"); 
    yield return "one"; 
    Console.WriteLine("GetPaths() Returning 'two'"); 
    yield return "two"; 
    Console.WriteLine("GetPaths() Returning 'three'"); 
    yield return "three"; 
} 

public bool IsWritable(string path) => false; // testing only 

Si je lance:

var paths = GetPaths(); 
Console.WriteLine("Searching for existing file.."); 
foreach (var path in paths) 
{ 
    if (File.Exists(path)) 
    { 
     Console.WriteLine($"Found existing file '{path}'"); 
    } 
} 

Console.WriteLine("Searching for a writable path.."); 
foreach (var path in paths.Reverse()) // NOTE: paths enumarated twice 
{ 
    if (IsWritable(path)) 
    { 
     Console.WriteLine($"Found writable path '{path}'"); 
    } 
} 

Console.WriteLine("No paths found"); 

Si le fichier 'un' existe, nous obtenons:

Searching for existing file.. 
Returning 'one' 
Found existing file 'one' 

Si, cependant, aucun fichier n'existe, nous obtenons:

Searching for existing file.. 
Returning 'one' 
Returning 'two' 
Returning 'three' 
Searching for a writable path.. 
Returning 'one' 
Returning 'two' 
Returning 'three' 
No paths found 

(nous énumérons gaspillée les résultats de GetPaths() deux fois)


Une solution simple est de changer la première ligne à:

var paths = GetPaths().ToList(); 

Cependant, cela signifie que même si le fichier one existe, la sortie serait:

Returning 'one' 
Returning 'two' 
Returning 'three' 
Searching for existing file.. 
Found existing file 'one' 

(comme, nous énumérons inutilement la r emainder de la liste)


est-il un (moyen intégré) pour obtenir à la fois l'énumération paresseux et toujours empêcher l'énumération multiple?

En d'autres termes, la sortie désirée lorsque 'un' existe est:

Searching for existing file.. 
Returning 'one' 
Found existing file 'one' 

et si aucun fichier existe:

Searching for existing file.. 
Returning 'one' 
Returning 'two' 
Returning 'three' 
Searching for a writable path.. 
No paths found 
+0

Oui. Appelez 'ToArray' sur l'énumérable. – Will

+2

Je ne crois pas qu'il y ait quelque chose de intégré pour ça, non. Vous pourriez l'appliquer vous-même, mais c'est une question différente. –

+4

@Will: En quoi est-ce encore paresseux? Cela équivaut (en termes d'évaluation) à appeler 'ToList()' que l'OP a déjà expliqué ... –

Répondre

0

Oui il y a. Si l'énumérateur renvoyé par votre appel Lazy obtient ses données à tour de rôle à partir de la même instance d'un fournisseur de données sous-jacent, et que ce fournisseur suit chaque élément, toutes les instances de l'énumérateur extraient des données de la même instance du fournisseur de données. . Considérons un énumérateur pour renvoyer des lignes à partir d'un fichier;

Si cet énumérateur partageait la même instance de Stream avec tous les autres énumérateurs pour ce fichier, vous obtiendriez le comportement que vous illustrez.

En pseudo-code

static System.IO.FileStream fs; 
private static void GetFileStream() 
{ 
    if(fs == null) fs = System.IO.File.Open(....); 
    return fs; 
} 

public Enumarable<string> GetLines() 
{ 
    // return an enumerator that uses the result of GetFileStream() and 
    // advances the file pointer. All Enumerables returned by this method 
    // will return unique lines from the same file 
} 
0

je peut-être manqué quelque chose, mais pourquoi ne pas utiliser la méthode FirstOrDefault d'extension de System.Linq.Enumerable?

public static bool IsWritable(string path) 
    { 
     Func<string, bool> canBeWrittenTo = p => true;// (details omitted) 
     return System.IO.File.Exists(path) && canBeWrittenTo(path); 
    } 

Puis:

 var firstWritablePath = GetPaths().FirstOrDefault(path => IsWritable(path)); 
     if (firstWritablePath != null) 
     { 
      // Found existing file... 
     } 
     else 
     { 
      // No paths found... 
     }