This article is available in english.
Parfois, les exemples les plus simples sont les meilleurs.
Mettons que vous avez un fichier de configuration, et que vous voulez en faire une copie avant de le modifier. Facile, vous copiez le fichier en “filename.bak”. Mais que se passe-t-il si ce fichier existe déjà ? Soit vous l’écrasez, soit vous créez un nom de fichier auto-incrémenté.
Si l’on veut faire ce dernier, il est possible de le faire avec une boucle for. Mais comme vous êtes un bon programmeur fonctionnel, vous allez le faire en utilisant LINQ.
On peut donc écrire ceci :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public static string CreateNewFileName(string filePath) { if (!File.Exists(filePath)) return filePath; // On évite de faire cela pour tous les fichiers var name = Path.GetFileNameWithoutExtension(filePath); var extension = Path.GetExtension(filePath); // Maintenant on cherche le fichier suivant var fileQuery = from index in Enumerable.Range(2, 10000) // On construit le nom let fileName = string.Format("{0} ({1}){2}", name, index, extension) // Est-ce que le fichier existe ? where !File.Exists(fileName) // Non ? On le sélectionne. select fileName; // On retourne le premier. return fileQuery.First(); } |
Il faut noter l'utilisation du mot clé “let” qui permet de réutiliser ce que l’on appelle une “range variable”. Dans ce cas, cela permet d’éviter d’appeler string.Format plusieurs fois.
Le cas de l’infini
Il y a malgré tout un petit problème dans cette implémentation, qui prend la forme du “10000” arbitraire. Cela peut-être tout à fait correct si vous n’avez pas l’intention de faire 10.000 fichiers de backup. Mais si c’est le cas, pour lever cette limite, on peut écrire l’itérateur suivant :
1 2 3 4 5 6 7 | public static IEnumerable<int> InfiniteRange(int start) { while(true) { yield return start++; } }
|
Qui fait en sorte de retourner une nouvelle valeur à chaque fois qu’une est demandée. Pour utiliser cette méthode, il faut bien être sur d’avoir une condition de sortie (le fichier n’existe pas, dans l’exemple précédent), sinon vous pourriez bien énumérer jusqu'à la fin des temps... En fait jusqu'à int.MaxValue, pour les nit-pickers, mais .NET 4.0 propose System.Numerics.BigInteger pour être bien sur d’arriver à la fin de temps. On ne sait jamais.
Enfin, pour utiliser cet itérateur, il faut simplement replacer :
1 | var fileQuery = from index in Enumerable.Range(2, 10000) |
par
1 | var fileQuery = from index in InfiniteRange(2) |
Et c’est terminé !