From 1adc5ca8f90469ea19e1090840de591829fcc179 Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Wed, 24 Jun 2015 06:38:11 +0200 Subject: [PATCH 01/11] =?UTF-8?q?Copie=20du=20projet=20pour=20pr=C3=A9pare?= =?UTF-8?q?r=20la=20version=20Technet.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Enumerables-Technet/README.md | 5 + Enumerables-Technet/Source/Enumerables.sln | 34 +++ .../Source/PrgPart1/App.config | 6 + .../Source/PrgPart1/PrgPart1.csproj | 61 +++++ .../Source/PrgPart1/Program.cs | 126 ++++++++++ .../PrgPart1/Properties/AssemblyInfo.cs | 36 +++ .../Source/PrgPart1/ReverseEnumerable.cs | 47 ++++ .../Source/PrgPart1/ReverseEnumerator.cs | 107 +++++++++ .../Source/PrgPart2/App.config | 6 + .../Source/PrgPart2/EnumFichierBarbare.cs | 90 +++++++ .../Source/PrgPart2/EnumFichierSubtile.cs | 209 +++++++++++++++++ .../PrgPart2/EnumFichierUnPeuMoinsBarbare.cs | 84 +++++++ .../Source/PrgPart2/Files/Fichier1.txt | 3 + .../Source/PrgPart2/Files/Fichier2.txt | 3 + .../Source/PrgPart2/Files/Fichier3.txt | 3 + .../Source/PrgPart2/PrgPart2.csproj | 73 ++++++ .../Source/PrgPart2/Program.cs | 221 ++++++++++++++++++ .../PrgPart2/Properties/AssemblyInfo.cs | 36 +++ .../Source/PrgPart3/App.config | 6 + Enumerables-Technet/Source/PrgPart3/Person.cs | 15 ++ .../Source/PrgPart3/PrgPart3.csproj | 60 +++++ .../Source/PrgPart3/Program.cs | 118 ++++++++++ .../PrgPart3/Properties/AssemblyInfo.cs | 36 +++ 23 files changed, 1385 insertions(+) create mode 100644 Enumerables-Technet/README.md create mode 100644 Enumerables-Technet/Source/Enumerables.sln create mode 100644 Enumerables-Technet/Source/PrgPart1/App.config create mode 100644 Enumerables-Technet/Source/PrgPart1/PrgPart1.csproj create mode 100644 Enumerables-Technet/Source/PrgPart1/Program.cs create mode 100644 Enumerables-Technet/Source/PrgPart1/Properties/AssemblyInfo.cs create mode 100644 Enumerables-Technet/Source/PrgPart1/ReverseEnumerable.cs create mode 100644 Enumerables-Technet/Source/PrgPart1/ReverseEnumerator.cs create mode 100644 Enumerables-Technet/Source/PrgPart2/App.config create mode 100644 Enumerables-Technet/Source/PrgPart2/EnumFichierBarbare.cs create mode 100644 Enumerables-Technet/Source/PrgPart2/EnumFichierSubtile.cs create mode 100644 Enumerables-Technet/Source/PrgPart2/EnumFichierUnPeuMoinsBarbare.cs create mode 100644 Enumerables-Technet/Source/PrgPart2/Files/Fichier1.txt create mode 100644 Enumerables-Technet/Source/PrgPart2/Files/Fichier2.txt create mode 100644 Enumerables-Technet/Source/PrgPart2/Files/Fichier3.txt create mode 100644 Enumerables-Technet/Source/PrgPart2/PrgPart2.csproj create mode 100644 Enumerables-Technet/Source/PrgPart2/Program.cs create mode 100644 Enumerables-Technet/Source/PrgPart2/Properties/AssemblyInfo.cs create mode 100644 Enumerables-Technet/Source/PrgPart3/App.config create mode 100644 Enumerables-Technet/Source/PrgPart3/Person.cs create mode 100644 Enumerables-Technet/Source/PrgPart3/PrgPart3.csproj create mode 100644 Enumerables-Technet/Source/PrgPart3/Program.cs create mode 100644 Enumerables-Technet/Source/PrgPart3/Properties/AssemblyInfo.cs diff --git a/Enumerables-Technet/README.md b/Enumerables-Technet/README.md new file mode 100644 index 0000000..3af301b --- /dev/null +++ b/Enumerables-Technet/README.md @@ -0,0 +1,5 @@ +Enumérables - Version Technet +============================= + +Ce projet est une refonte des articles sur les énumérables pour une version Wiki Technet. + diff --git a/Enumerables-Technet/Source/Enumerables.sln b/Enumerables-Technet/Source/Enumerables.sln new file mode 100644 index 0000000..8f2dc26 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrgPart1", "PrgPart1\PrgPart1.csproj", "{07FF9A43-3798-4B14-AEB6-A41C2A9547BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrgPart2", "PrgPart2\PrgPart2.csproj", "{813646C0-C8E7-4EC7-AD63-B021AE490099}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrgPart3", "PrgPart3\PrgPart3.csproj", "{41349392-BB15-4575-9B6D-D3B734096B90}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {07FF9A43-3798-4B14-AEB6-A41C2A9547BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07FF9A43-3798-4B14-AEB6-A41C2A9547BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07FF9A43-3798-4B14-AEB6-A41C2A9547BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07FF9A43-3798-4B14-AEB6-A41C2A9547BA}.Release|Any CPU.Build.0 = Release|Any CPU + {813646C0-C8E7-4EC7-AD63-B021AE490099}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {813646C0-C8E7-4EC7-AD63-B021AE490099}.Debug|Any CPU.Build.0 = Debug|Any CPU + {813646C0-C8E7-4EC7-AD63-B021AE490099}.Release|Any CPU.ActiveCfg = Release|Any CPU + {813646C0-C8E7-4EC7-AD63-B021AE490099}.Release|Any CPU.Build.0 = Release|Any CPU + {41349392-BB15-4575-9B6D-D3B734096B90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41349392-BB15-4575-9B6D-D3B734096B90}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41349392-BB15-4575-9B6D-D3B734096B90}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41349392-BB15-4575-9B6D-D3B734096B90}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Enumerables-Technet/Source/PrgPart1/App.config b/Enumerables-Technet/Source/PrgPart1/App.config new file mode 100644 index 0000000..9c05822 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart1/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart1/PrgPart1.csproj b/Enumerables-Technet/Source/PrgPart1/PrgPart1.csproj new file mode 100644 index 0000000..91b4420 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart1/PrgPart1.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {07FF9A43-3798-4B14-AEB6-A41C2A9547BA} + Exe + Properties + PrgPart1 + PrgPart1 + v4.5.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart1/Program.cs b/Enumerables-Technet/Source/PrgPart1/Program.cs new file mode 100644 index 0000000..731a901 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart1/Program.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart1 +{ + class Program + { + /// + /// Retourne une liste d'éléments + /// + static int[] GetItems() + { + return new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + } + + /// + /// Exemple foreach + /// + static void ForEach() + { + // Récupération de l'énumérable + IEnumerable enumerable = GetItems(); + + // Parcours chaque élément dans elm + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + } + + /// + /// Exemple d'itération de base + /// + static void IterationBase() + { + // Récupération de l'énumérable + IEnumerable enumerable = GetItems(); + + // Récupère un nouvel énumérateur + IEnumerator enumerator = enumerable.GetEnumerator(); + // Tant que l'énumérateur se déplace + while (enumerator.MoveNext()) + { + Int32 elm = enumerator.Current; + // .. + Console.WriteLine(elm); + } + } + + /// + /// Exemple d'itération avec gestion du IDisposable tel que le fait un foreach + /// + static void IterationBaseWithDispose() + { + // Récupération de l'énumérable + IEnumerable enumerable = GetItems(); + + // Récupère un nouvel énumérateur + IEnumerator enumerator = enumerable.GetEnumerator(); + try + { + // Tant que l'énumérateur se déplace + while (enumerator.MoveNext()) + { + Int32 elm = enumerator.Current; + // .. + Console.WriteLine(elm); + } + } + finally + { + // On détermine si l'énumérateur est disposable + IDisposable disp = enumerator as IDisposable; + // Si c'est le cas on dispose l'énumérateur + if (disp != null) + { + disp.Dispose(); + } + } + } + + /// + /// Test ReverseEnumerator + /// + static void TestReverse() + { + // ReverseEnumerator attend un IList<>, aussi on récupère le tableau comme IList. + // Tous les tableaux .Net sont des IList<> donc il n'y a pas de problème de cast + IList list = GetItems(); + + // Création de l'énumerable reverse + IEnumerable enumerable = new ReverseEnumerable(list); + + // Parcours chaque élément dans elm + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + + // On parcours chaque élément mais en arrêtant la boucle quand on trouve l'élément 5 + foreach (int elm in enumerable) + { + if (elm == 5) break; + // .. + Console.WriteLine(elm); + } + + } + + static void Main(string[] args) + { + //ForEach(); + //IterationBase(); + //IterationBaseWithDispose(); + TestReverse(); + + Console.ReadLine(); + } + + } +} diff --git a/Enumerables-Technet/Source/PrgPart1/Properties/AssemblyInfo.cs b/Enumerables-Technet/Source/PrgPart1/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e238176 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart1/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Les informations générales relatives à un assembly dépendent de +// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations +// associées à un assembly. +[assembly: AssemblyTitle("PrgPart1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PrgPart1")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly +// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de +// COM, affectez la valeur true à l'attribut ComVisible sur ce type. +[assembly: ComVisible(false)] + +// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM +[assembly: Guid("b0b96a67-03b4-469d-8a73-2cf41fe62c9c")] + +// Les informations de version pour un assembly se composent des quatre valeurs suivantes : +// +// Version principale +// Version secondaire +// Numéro de build +// Révision +// +// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut +// en utilisant '*', comme indiqué ci-dessous : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Enumerables-Technet/Source/PrgPart1/ReverseEnumerable.cs b/Enumerables-Technet/Source/PrgPart1/ReverseEnumerable.cs new file mode 100644 index 0000000..ce619e5 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart1/ReverseEnumerable.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart1 +{ + /// + /// Implémentation de IEnumerable permettant de parcourir une liste dans le sens inverse + /// + public class ReverseEnumerable : IEnumerable + { + + /// + /// Nouvelle classe énumérable + /// + public ReverseEnumerable(IList source) + { + this.List = source; + } + + /// + /// Implémentation de IEnumerable<T>.GetEnumerator() + /// + /// Créé un nouveau ReverseEnumerator + public IEnumerator GetEnumerator() + { + return new ReverseEnumerator(List); + } + + /// + /// IEnumerable.GetEnumerator() (version non générique) + /// + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Liste source + /// + public IList List { get; private set; } + + } +} diff --git a/Enumerables-Technet/Source/PrgPart1/ReverseEnumerator.cs b/Enumerables-Technet/Source/PrgPart1/ReverseEnumerator.cs new file mode 100644 index 0000000..d036846 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart1/ReverseEnumerator.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart1 +{ + + /// + /// Enumérateur parcourant une liste dans le sens inverse + /// + public class ReverseEnumerator : IEnumerator + { + IList _Source; + int _Position; + bool _Completed; + + /// + /// Création d'un nouvel énumérateur + /// + public ReverseEnumerator(IList source) + { + this._Source = source; + // On met -1 pour indiquer qu'on a pas commencé l'itération + this._Position = -1; + // L'itération n'est pas terminée + this._Completed = false; + // On défini Current avec la valeur par défaut + this.Current = default(T); + } + + /// + /// Libération des ressources + /// + public void Dispose() + { + // On a rien à libérer , mais on marque notre itérateur comme terminé + this._Completed = true; + Console.WriteLine("Enumerateur disposé"); + } + + /// + /// Cette méthode est appelée lorsque l'on veut réinitialiser l'énumérateur + /// + public void Reset() + { + // On met -1 pour indiquer qu'on a pas commencer l'itération + this._Position = -1; + // L'itération n'est pas terminée + this._Completed = false; + // On défini Current avec la valeur par défaut + this.Current = default(T); + } + + /// + /// On se déplace vers le prochain élément + /// + /// False lorsque l'itération est terminée + public bool MoveNext() + { + // Si la source est Null alors on a rien à parcourir, donc l'itération s'arrête + if (this._Source == null) return false; + + // Si l'itération est terminée alors on ne va pas plus loin + if (this._Completed) return false; + + // Si la position est à -1 on récupère le nombre d'éléments à parcourir pour démarrer l'itération + if (this._Position == -1) + { + Console.WriteLine("Itération commencée"); + this._Position = _Source.Count; + } + + // On se déplace dans la liste + this._Position--; + + // Si on a atteind -1 alors on a terminé l'itération + if (this._Position < 0) + { + this._Completed = true; + Console.WriteLine("Itération terminée"); + return false; + } + + // On défini Current et on continue + Current = this._Source[this._Position]; + + return true; + } + + /// + /// Elément en cours de l'itération + /// + public T Current { get; private set; } + + /// + /// Elément en cours pour la version non générique + /// + object System.Collections.IEnumerator.Current + { + get { return Current; } + } + + } + +} diff --git a/Enumerables-Technet/Source/PrgPart2/App.config b/Enumerables-Technet/Source/PrgPart2/App.config new file mode 100644 index 0000000..9c05822 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart2/EnumFichierBarbare.cs b/Enumerables-Technet/Source/PrgPart2/EnumFichierBarbare.cs new file mode 100644 index 0000000..2ade2ea --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/EnumFichierBarbare.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart2 +{ + /// + /// Enumérable parcourant les lignes texte d'un ensemble de fichier + /// + public class EnumFichierBarbare : IEnumerable + { + + private List _Lines; + + /// + /// Création d'un nouvel énumérable + /// + public EnumFichierBarbare(String[] files) + { + // Initialisation des fichiers + this.Files = files; + + // On marque la liste des lignes à charger en la mettant à null + _Lines = null; + } + + void LoadFiles() + { + // Création de la liste des lignes + _Lines = new List(); + + if (this.Files != null) + { + // Pour chaque fichier + foreach (var file in Files) + { + try + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + _Lines.Add(line); + } + } + } + catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier + } + } + } + + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // Si la liste des lignes est null alors il faut lire les fichiers + if (_Lines == null) + { + // Chargement des fichiers + LoadFiles(); + } + + // Retourne l'énumérateur de la liste + return _Lines.GetEnumerator(); + } + + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + + } + +} diff --git a/Enumerables-Technet/Source/PrgPart2/EnumFichierSubtile.cs b/Enumerables-Technet/Source/PrgPart2/EnumFichierSubtile.cs new file mode 100644 index 0000000..7f43275 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/EnumFichierSubtile.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart2 +{ + /// + /// Enumérable parcourant les lignes texte d'un ensemble de fichier via un énumérateur + /// + public class EnumFichierSubtile : IEnumerable + { + + /// + /// Création d'un nouvel énumérable + /// + public EnumFichierSubtile(String[] files) + { + // Initialisation des fichiers + this.Files = files; + } + + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // Retourne un nouvel énumérateur + return new FichierEnumerator(Files); + } + + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + + } + + /// + /// Enumérateur de fichier + /// + class FichierEnumerator : IEnumerator + { + Func _CurrentState = null; + int _CurrentFilePos; + String _CurrentFileName; + TextReader _CurrentFile; + + /// + /// Création d'un nouvel énumérateur + /// + public FichierEnumerator(String[] files) + { + // Initialisation des fichiers + this.Files = files; + // Initialisation de l'énumérateur + Current = null; + _CurrentFilePos = 0; + _CurrentFileName = null; + _CurrentFile = null; + // L'état de l'énumérateur est à l'ouverture du prochain fichier à traiter + _CurrentState = OpenNextFileState; + } + + /// + /// Libération des ressources éventuelles + /// + public void Dispose() + { + // Si on a un fichier encore d'ouvert on libère la mémoire + if (_CurrentFile != null) + { + _CurrentFile.Dispose(); + _CurrentFile = null; + } + // On défini l'état 'Completed' + _CurrentState = CompletedState; + } + + /// + /// Essayes d'ouvrir le prochain fichier de la liste + /// + bool GetNextFile() + { + String filename = null; + TextReader file = null; + while (file == null && Files != null && _CurrentFilePos < Files.Length) + { + try + { + filename = Files[_CurrentFilePos++]; + file = new StreamReader(filename); + } + catch { } + } + // Si on a un fichier d'ouvert + if (file != null) + { + _CurrentFileName = filename; + _CurrentFile = file; + return true; + } + // Sinon on a rien trouvé + return false; + } + + /// + /// Ouverture du prochain fichier + /// + bool OpenNextFileState() + { + // Si on a pas ou plus de fichier on arrête tout + if (!GetNextFile()) + { + Current = null; + // On passe à l'état 'Completed' + _CurrentState = CompletedState; + // On termine + return false; + } + + // On passe à l'état ReadNextLine + _CurrentState = ReadNextLineState; + + // On lit la première ligne + return _CurrentState(); + } + + /// + /// On est en cours de lecture + /// + bool ReadNextLineState() + { + try + { + // On lit la prochaine ligne du fichier + String line = _CurrentFile.ReadLine(); + // Si la ligne n'est pas null on la traite + if (line != null) + { + Current = line; + return true; + } + // La ligne est null alors on a atteint la fin du fichier, on libère sa ressource + } + catch + { + // Si une erreur survient à la lecture on ferme le fichier en cours pour éviter les boucles infinies + + } + // Libération des ressources pour passer au fichier suivant + _CurrentFile.Dispose(); + _CurrentFile = null; + _CurrentFileName = null; + // On passe à l'état 'OpenNextFile' + _CurrentState = OpenNextFileState; + // On traite le prochain état + return _CurrentState(); + } + + /// + /// L'itération est terminée on retourne toujours false + /// + bool CompletedState() + { + return false; + } + + /// + /// On ne s'occupe pas de cette méthode + /// + public void Reset() + { + throw new NotSupportedException(); + } + + /// + /// On se déplace + /// + public bool MoveNext() + { + // Exécution de l'état en cours + return _CurrentState(); + } + + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + + /// + /// Valeur en cours + /// + public string Current { get; private set; } + object System.Collections.IEnumerator.Current { get { return Current; } } + + } + +} diff --git a/Enumerables-Technet/Source/PrgPart2/EnumFichierUnPeuMoinsBarbare.cs b/Enumerables-Technet/Source/PrgPart2/EnumFichierUnPeuMoinsBarbare.cs new file mode 100644 index 0000000..d7ac9bd --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/EnumFichierUnPeuMoinsBarbare.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart2 +{ + /// + /// Enumérable parcourant les lignes texte d'un ensemble de fichier reconstruisant une liste à chaque appel + /// + public class EnumFichierUnPeuMoinsBarbare : IEnumerable + { + + /// + /// Création d'un nouvel énumérable + /// + public EnumFichierUnPeuMoinsBarbare(String[] files) + { + // Initialisation des fichiers + this.Files = files; + } + + IList LoadFiles() + { + // Création de la liste des lignes + var result = new List(); + + if (this.Files != null) + { + // Pour chaque fichier + foreach (var file in Files) + { + try + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + result.Add(line); + } + } + } + catch { } // Si une erreur à lecture du fichier on passe au prochain fichier + } + } + + // On retourne la liste + return result; + } + + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // On construit la liste + var list = LoadFiles(); + + // Retourne l'énumérateur de la liste + return list.GetEnumerator(); + } + + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + + } + +} diff --git a/Enumerables-Technet/Source/PrgPart2/Files/Fichier1.txt b/Enumerables-Technet/Source/PrgPart2/Files/Fichier1.txt new file mode 100644 index 0000000..6241aba --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/Files/Fichier1.txt @@ -0,0 +1,3 @@ +Première ligne du premier fichier. +Deuxième ligne du premier fichier. +Troisième ligne du premier fichier. diff --git a/Enumerables-Technet/Source/PrgPart2/Files/Fichier2.txt b/Enumerables-Technet/Source/PrgPart2/Files/Fichier2.txt new file mode 100644 index 0000000..6cf2956 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/Files/Fichier2.txt @@ -0,0 +1,3 @@ +Première ligne du second fichier. +Deuxième ligne du second fichier. +Troisième ligne du second fichier. diff --git a/Enumerables-Technet/Source/PrgPart2/Files/Fichier3.txt b/Enumerables-Technet/Source/PrgPart2/Files/Fichier3.txt new file mode 100644 index 0000000..f984105 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/Files/Fichier3.txt @@ -0,0 +1,3 @@ +Première ligne du troisième fichier. +Deuxième ligne du troisième fichier. +Troisième ligne du troisième fichier. diff --git a/Enumerables-Technet/Source/PrgPart2/PrgPart2.csproj b/Enumerables-Technet/Source/PrgPart2/PrgPart2.csproj new file mode 100644 index 0000000..ece0eb5 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/PrgPart2.csproj @@ -0,0 +1,73 @@ + + + + + Debug + AnyCPU + {813646C0-C8E7-4EC7-AD63-B021AE490099} + Exe + Properties + PrgPart2 + PrgPart2 + v4.5.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart2/Program.cs b/Enumerables-Technet/Source/PrgPart2/Program.cs new file mode 100644 index 0000000..3495b10 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/Program.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart2 +{ + class Program + { + + static String[] GetTestFileNames() + { + var dir = AppDomain.CurrentDomain.BaseDirectory; + return new String[]{ + Path.Combine(dir, "Files", "Fichier1.txt"), + Path.Combine(dir, "Files", "Fichier2.txt"), + Path.Combine(dir, "Files", "Fichier3.txt") + }; + } + + private static void TestFichierBarbare() + { + // Création de l'énumérable avec les fichiers de tests + var enumerable = new EnumFichierBarbare(GetTestFileNames()); + + // On parcours l'énumérable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + + } + + private static void TestFichierUnPeuMoinsBarbare() + { + // Création de l'énumérable avec les fichiers de tests + var enumerable = new EnumFichierUnPeuMoinsBarbare(GetTestFileNames()); + + // On parcours l'énumérable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + + } + + private static void TestFichierSubtile() + { + // Création de l'énumérable avec les fichiers de tests + var enumerable = new EnumFichierSubtile(GetTestFileNames()); + + // On parcours l'énumérable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + + // On parcours l'énumérable et provoque un arrêt prématuré + int i = 0; + foreach (var line in enumerable) + { + if (i++ >= 4) break; + Console.WriteLine(line); + } + + } + + /// + /// Ouvre un nouveau fichier ou retourne null si une erreur à lieu + /// + static StreamReader OpenFile(String file) + { + try + { + return new StreamReader(file); + } + catch + { + return null; + } + } + + static IEnumerable EnumFichierYield(String[] files) + { + if (files != null) + { + // Pour chaque fichier + foreach (var file in files) + { + // Ouverture d'un lecteur de fichier texte + using (var reader = OpenFile(file)) + { + // reader peut être null si une erreur à eu lieu + if (reader != null) + { + // Lecture de chaque ligne du fichier + String line; + do + { + // Lecture d'une ligne, si une erreur à lieu on arrête la boucle + try + { + line = reader.ReadLine(); + } + catch + { + break; + } + // On envoi la ligne d'énumérable + if (line != null) + yield return line; + } while (line != null);// Boucle tant qu'on a une ligne + } + } + } + } + } + + static IEnumerable EnumFichierYieldUnprotected(String[] files) + { + if (files != null) + { + // Pour chaque fichier + foreach (var file in files) + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + yield return line; + } + } + } + } + } + + private static void TestFichierYield() + { + // Récupération d'un 'énumérable avec les fichiers de tests + var enumerable = EnumFichierYield(GetTestFileNames()); + + // On parcours l'énumérable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + + // On parcours l'énumérable et provoque un arrêt prématuré + int i = 0; + foreach (var line in enumerable) + { + if (i++ >= 4) break; + Console.WriteLine(line); + } + + } + + static IEnumerable EnumComplex() + { + Random rnd = new Random(); + + // Emission directe + yield return "Start"; + + // Emission dans un switch + for (int i = 0; i < 10; i++) + { + switch (rnd.Next(4)) + { + case 1: + yield return "Funky 1"; + break; + case 2: + continue; + case 3: + yield return "<<<<"; + // Emission d'un autre énum + foreach (var line in EnumFichierYield(GetTestFileNames())) + { + // On place une condition + if (line.Contains("x")) + yield return line; + } + yield return ">>>>"; + break; + case 0: + default: + yield return "Funky 0"; + break; + } + } + + // Emission directe + yield return "End"; + } + + static void TestEnumComplex() + { + foreach (var item in EnumComplex()) + Console.WriteLine(item); + } + + static void Main(string[] args) + { + //TestFichierBarbare(); + //TestFichierUnPeuMoinsBarbare(); + //TestFichierSubtile(); + //TestFichierYield(); + TestEnumComplex(); + + Console.ReadLine(); + } + + } +} diff --git a/Enumerables-Technet/Source/PrgPart2/Properties/AssemblyInfo.cs b/Enumerables-Technet/Source/PrgPart2/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a55a358 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart2/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Les informations générales relatives à un assembly dépendent de +// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations +// associées à un assembly. +[assembly: AssemblyTitle("PrgPart2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PrgPart2")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly +// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de +// COM, affectez la valeur true à l'attribut ComVisible sur ce type. +[assembly: ComVisible(false)] + +// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM +[assembly: Guid("3e47d205-0d3d-4ea8-9fbd-cb6565cce4db")] + +// Les informations de version pour un assembly se composent des quatre valeurs suivantes : +// +// Version principale +// Version secondaire +// Numéro de build +// Révision +// +// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut +// en utilisant '*', comme indiqué ci-dessous : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Enumerables-Technet/Source/PrgPart3/App.config b/Enumerables-Technet/Source/PrgPart3/App.config new file mode 100644 index 0000000..9c05822 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart3/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart3/Person.cs b/Enumerables-Technet/Source/PrgPart3/Person.cs new file mode 100644 index 0000000..4d7966f --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart3/Person.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart3 +{ + class Person + { + public String Name { get; set; } + public DateTime? BirthDay { get; set; } + public bool IsFamily { get; set; } + } +} diff --git a/Enumerables-Technet/Source/PrgPart3/PrgPart3.csproj b/Enumerables-Technet/Source/PrgPart3/PrgPart3.csproj new file mode 100644 index 0000000..ab39d74 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart3/PrgPart3.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + {41349392-BB15-4575-9B6D-D3B734096B90} + Exe + Properties + PrgPart3 + PrgPart3 + v4.5.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart3/Program.cs b/Enumerables-Technet/Source/PrgPart3/Program.cs new file mode 100644 index 0000000..1c47689 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart3/Program.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PrgPart3 +{ + class Program + { + static IEnumerable GetPersons() + { + yield return new Person() { + Name = "Toto", + BirthDay = null, + IsFamily = true + }; + yield return new Person() { + Name = "Titi", + BirthDay = new DateTime(2010, 11, 27), + IsFamily = false + }; + yield return new Person() { + Name = "Tata", + BirthDay = null, + IsFamily = false + }; + yield return new Person() { + Name = "Tutu", + BirthDay = new DateTime(1994, 8, 11), + IsFamily = true + }; + } + + static void TestLinqRequest() + { + var query = from p in GetPersons() + where p.IsFamily + orderby p.Name + select p; + + foreach (var p in query) + Console.WriteLine(p.Name); + + foreach (var p in query.Take(2)) + Console.WriteLine(p.Name); + } + + static void TestLinqRequestAsFluent() + { + var query = GetPersons() + .Where(p => p.IsFamily) + .OrderBy(p => p.Name) + ; + + foreach (var p in query) + Console.WriteLine(p.Name); + + foreach (var p in query.Take(2)) + Console.WriteLine(p.Name); + } + + //static void TestStats() + //{ + // var q = from stats in GetStats() + // where stat.Date >= lastRefresh + // orderby stat.Date + // select stats; + + // foreach (var stats in q) + // { + // // Affichage de la stat + // } + + // lblLastUpdate.Text = String.Format("{0} : {1} nouvelles statistiques", DateTime.Now, q.Count()); + // lastRefresh = DateTime.Now; + + //} + + static IEnumerable SimulWhere() + { + foreach (var p in GetPersons()) + { + if(p.IsFamily) + yield return p; + } + } + + static IEnumerable SimulOrderBy() + { + List result = new List(); + result.AddRange(SimulWhere()); + result.Sort((x, y) => String.Compare(x.Name, y.Name)); + foreach (var p in result) + yield return p; + } + + static void TestSimulLinq() + { + var query = SimulOrderBy(); + + foreach (var p in query) + Console.WriteLine(p.Name); + + foreach (var p in query.Take(2)) + Console.WriteLine(p.Name); + } + + static void Main(string[] args) + { + //TestLinqRequest(); + //TestLinqRequestAsFluent(); + TestSimulLinq(); + + Console.ReadLine(); + } + } +} diff --git a/Enumerables-Technet/Source/PrgPart3/Properties/AssemblyInfo.cs b/Enumerables-Technet/Source/PrgPart3/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..43f64c6 --- /dev/null +++ b/Enumerables-Technet/Source/PrgPart3/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Les informations générales relatives à un assembly dépendent de +// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations +// associées à un assembly. +[assembly: AssemblyTitle("PrgPart3")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PrgPart3")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly +// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de +// COM, affectez la valeur true à l'attribut ComVisible sur ce type. +[assembly: ComVisible(false)] + +// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM +[assembly: Guid("5406360f-dd7b-4149-94c4-3bba295c3a8d")] + +// Les informations de version pour un assembly se composent des quatre valeurs suivantes : +// +// Version principale +// Version secondaire +// Numéro de build +// Révision +// +// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut +// en utilisant '*', comme indiqué ci-dessous : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] From 6bef87e04475cd6eaecf7387ff874a41afd90233 Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Wed, 24 Jun 2015 06:49:33 +0200 Subject: [PATCH 02/11] =?UTF-8?q?Pr=C3=A9paration=20du=20programme=20d'exe?= =?UTF-8?q?mple=20en=20fran=C3=A7ais.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Enumerables-fr/App.config | 6 ++ .../Enumerables-fr/Enumerables-fr.csproj | 59 +++++++++++++++++++ .../Source/Enumerables-fr/Program.cs | 45 ++++++++++++++ .../Enumerables-fr/Properties/AssemblyInfo.cs | 36 +++++++++++ Enumerables-Technet/Source/Enumerables.sln | 6 ++ 5 files changed, 152 insertions(+) create mode 100644 Enumerables-Technet/Source/Enumerables-fr/App.config create mode 100644 Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj create mode 100644 Enumerables-Technet/Source/Enumerables-fr/Program.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/Properties/AssemblyInfo.cs diff --git a/Enumerables-Technet/Source/Enumerables-fr/App.config b/Enumerables-Technet/Source/Enumerables-fr/App.config new file mode 100644 index 0000000..9c05822 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj b/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj new file mode 100644 index 0000000..92487c2 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj @@ -0,0 +1,59 @@ + + + + + Debug + AnyCPU + {BD6CD85F-9196-4DFB-BEDC-560004EB982A} + Exe + Properties + Enumerables + Enumerables-fr + v4.5.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/Enumerables-fr/Program.cs b/Enumerables-Technet/Source/Enumerables-fr/Program.cs new file mode 100644 index 0000000..7d5299e --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/Program.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + class Program + { + + /// + /// Retourne une liste d'éléments + /// + static int[] GetItems() + { + return new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + } + + /// + /// Exemple foreach + /// + static void ForEach() + { + // Récupération de l'énumérable + IEnumerable enumerable = GetItems(); + + // Parcours chaque élément dans 'enumerable' + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + } + + static void Main(string[] args) + { + Console.WriteLine("* Exemple ForEach"); + ForEach(); + Console.WriteLine(); + + Console.ReadKey(); + } + } +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/Properties/AssemblyInfo.cs b/Enumerables-Technet/Source/Enumerables-fr/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..51beddc --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Les informations générales relatives à un assembly dépendent de +// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations +// associées à un assembly. +[assembly: AssemblyTitle("Enumerables-fr")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Enumerables-fr")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly +// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de +// COM, affectez la valeur true à l'attribut ComVisible sur ce type. +[assembly: ComVisible(false)] + +// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM +[assembly: Guid("b18cdda5-64a0-4310-9f54-314fa6fe6591")] + +// Les informations de version pour un assembly se composent des quatre valeurs suivantes : +// +// Version principale +// Version secondaire +// Numéro de build +// Révision +// +// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut +// en utilisant '*', comme indiqué ci-dessous : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Enumerables-Technet/Source/Enumerables.sln b/Enumerables-Technet/Source/Enumerables.sln index 8f2dc26..b8a086b 100644 --- a/Enumerables-Technet/Source/Enumerables.sln +++ b/Enumerables-Technet/Source/Enumerables.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrgPart2", "PrgPart2\PrgPar EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrgPart3", "PrgPart3\PrgPart3.csproj", "{41349392-BB15-4575-9B6D-D3B734096B90}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enumerables-fr", "Enumerables-fr\Enumerables-fr.csproj", "{BD6CD85F-9196-4DFB-BEDC-560004EB982A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {41349392-BB15-4575-9B6D-D3B734096B90}.Debug|Any CPU.Build.0 = Debug|Any CPU {41349392-BB15-4575-9B6D-D3B734096B90}.Release|Any CPU.ActiveCfg = Release|Any CPU {41349392-BB15-4575-9B6D-D3B734096B90}.Release|Any CPU.Build.0 = Release|Any CPU + {BD6CD85F-9196-4DFB-BEDC-560004EB982A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD6CD85F-9196-4DFB-BEDC-560004EB982A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD6CD85F-9196-4DFB-BEDC-560004EB982A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD6CD85F-9196-4DFB-BEDC-560004EB982A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d2e0b7c4cfc8fbdfffa8cfe42381353fe4bb25d2 Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Wed, 24 Jun 2015 20:39:55 +0200 Subject: [PATCH 03/11] Reprise du code de la partie 1. --- .../EnumerableWithEnumeratorDisposable.cs | 46 +++++ .../Enumerables-fr/Enumerables-fr.csproj | 6 + .../Enumerables-fr/EnumeratorDisposable.cs | 61 +++++++ .../Source/Enumerables-fr/FakeEnumerable.cs | 24 +++ .../Source/Enumerables-fr/FakeEnumerator.cs | 32 ++++ .../Source/Enumerables-fr/Program.cs | 160 +++++++++++++++++- .../Enumerables-fr/ReverseEnumerable.cs | 47 +++++ .../Enumerables-fr/ReverseEnumerator.cs | 107 ++++++++++++ 8 files changed, 478 insertions(+), 5 deletions(-) create mode 100644 Enumerables-Technet/Source/Enumerables-fr/EnumerableWithEnumeratorDisposable.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/EnumeratorDisposable.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/FakeEnumerable.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/FakeEnumerator.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/ReverseEnumerable.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/ReverseEnumerator.cs diff --git a/Enumerables-Technet/Source/Enumerables-fr/EnumerableWithEnumeratorDisposable.cs b/Enumerables-Technet/Source/Enumerables-fr/EnumerableWithEnumeratorDisposable.cs new file mode 100644 index 0000000..90c30fe --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/EnumerableWithEnumeratorDisposable.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Classe énumérable utilisant des énumérateurs disposable + /// + class EnumerableWithEnumeratorDisposable : IEnumerable + { + List _Source; + + /// + /// Création d'un nouvel énumérable + /// + /// Source d'initialisation + /// Action invoquée lorsqu'un énumérateur est disposé + public EnumerableWithEnumeratorDisposable(IEnumerable source, Action onDispose = null) + { + _Source = new List(source); + OnDispose = onDispose; + } + + /// + /// Retourne un énumérateur disposable + /// + public IEnumerator GetEnumerator() + { + return new EnumeratorDisposable(_Source, OnDispose); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Action invoquée lorsqu'un énumérateur est disposé + /// + public Action OnDispose { get; set; } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj b/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj index 92487c2..f5915ed 100644 --- a/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj +++ b/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj @@ -42,8 +42,14 @@ + + + + + + diff --git a/Enumerables-Technet/Source/Enumerables-fr/EnumeratorDisposable.cs b/Enumerables-Technet/Source/Enumerables-fr/EnumeratorDisposable.cs new file mode 100644 index 0000000..7c1402d --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/EnumeratorDisposable.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Enumérateur disposable + /// + class EnumeratorDisposable : IEnumerator, IDisposable + { + List _Source; + int _Current = -1; + + /// + /// Création d'un nouvel énuméraeur + /// + /// Source à énumérer + /// Action invoqué lors du Dispose + public EnumeratorDisposable(IEnumerable source, Action onDispose) + { + this._Source = new List(source); + this.OnDispose = onDispose; + } + /// + /// Dispose + /// + public void Dispose() + { + if (OnDispose != null) + OnDispose(); + } + /// + /// Item en cours + /// + public T Current { get; private set; } + object System.Collections.IEnumerator.Current { get { return Current; } } + /// + /// Déplacement vers le prochain item + /// + public bool MoveNext() + { + if (++_Current >= _Source.Count) return false; + Current = _Source[_Current]; + return true; + } + + public void Reset() + { + throw new NotImplementedException(); + } + + /// + /// Méthode invoquée lorsque l'énnumérateur est disposé + /// + public Action OnDispose { get; set; } + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/FakeEnumerable.cs b/Enumerables-Technet/Source/Enumerables-fr/FakeEnumerable.cs new file mode 100644 index 0000000..b997c3e --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/FakeEnumerable.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Faux énumérable n'implémentant pas IEnumerable mais supporté par foreach + /// + class FakeEnumerable + { + /// + /// foreach requierd uniquement une méthode publique GetEnumerator() qui retourne un objet qui contient une méthode publique + /// MoveNext() et une propriété Current. + /// + public FakeEnumerator GetEnumerator() + { + return new FakeEnumerator(); + } + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/FakeEnumerator.cs b/Enumerables-Technet/Source/Enumerables-fr/FakeEnumerator.cs new file mode 100644 index 0000000..161cef3 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/FakeEnumerator.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Enumerables +{ + /// + /// Faux énumérateur n'implémentant pas IEnumerator mais supporté par foreach + /// + /// + /// Doit implémenter une méthode publique MoveNext() et une propriété Current + /// + class FakeEnumerator + { + List _Source = new List(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + int _Current = -1; + /// + /// Item en cours + /// + public int Current { get; private set; } + /// + /// Déplacement vers le prochain item + /// + public bool MoveNext() + { + if (++_Current >= _Source.Count) return false; + Current = _Source[_Current]; + return true; + } + } +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/Program.cs b/Enumerables-Technet/Source/Enumerables-fr/Program.cs index 7d5299e..0e0fc26 100644 --- a/Enumerables-Technet/Source/Enumerables-fr/Program.cs +++ b/Enumerables-Technet/Source/Enumerables-fr/Program.cs @@ -10,13 +10,17 @@ class Program { /// - /// Retourne une liste d'éléments + /// Retourne une liste d'éléments en tant qu'enumérable /// - static int[] GetItems() + /// Indique si on retourne un énumérable avec énumérateur disposable + static IEnumerable GetItems(bool asDisposable = false) { - return new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + var items = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + return asDisposable ? new EnumerableWithEnumeratorDisposable(items, () => Console.WriteLine("Enumérateur Disposé")).AsEnumerable() : items; } + #region Exemple boucles + /// /// Exemple foreach /// @@ -33,13 +37,159 @@ static void ForEach() } } + /// + /// Exemple foreach avec des énumérateur disposable + /// + static void ForEachDisposable() + { + // Récupération de l'énumérable + IEnumerable enumerable = GetItems(true); + + // Parcours chaque élément dans 'enumerable' + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + } + + /// + /// Exemple d'itération de base + /// + static void IterationBase() + { + // Récupération de l'énumérable + IEnumerable enumerable = GetItems(); + + // Récupère un nouvel énumérateur + IEnumerator enumerator = enumerable.GetEnumerator(); + // Tant que l'énumérateur se déplace + while (enumerator.MoveNext()) + { + Int32 elm = enumerator.Current; + // .. + Console.WriteLine(elm); + } + } + + /// + /// Exemple d'itération avec gestion du IDisposable tel que le fait un foreach + /// + static void IterationBaseWithDispose() + { + // Récupération de l'énumérable + IEnumerable enumerable = GetItems(true); + + // Récupère un nouvel énumérateur + IEnumerator enumerator = enumerable.GetEnumerator(); + try + { + // Tant que l'énumérateur se déplace + while (enumerator.MoveNext()) + { + Int32 elm = enumerator.Current; + // .. + Console.WriteLine(elm); + } + } + finally + { + // On détermine si l'énumérateur est disposable + IDisposable disp = enumerator as IDisposable; + // Si c'est le cas on dispose l'énumérateur + if (disp != null) + { + disp.Dispose(); + } + } + } + + /// + /// Exemple de foreach avec un objet n'implémentant pas IEnumerable/IEnumerator + /// + static void ForEachWithoutIEnumerable() + { + // Création du faux énumérable + var enumerable = new FakeEnumerable(); + + // Parcours chaque élément dans 'enumerable' + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + } + + /// + /// Test ReverseEnumerator + /// + static void TestReverse() + { + // ReverseEnumerator attend un IList<> + IList list = new List(GetItems()); + + // Création de l'énumerable reverse + IEnumerable enumerable = new ReverseEnumerable(list); + + // Parcours chaque élément dans elm + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + + // On parcours chaque élément mais en arrêtant la boucle quand on trouve l'élément 5 + foreach (int elm in enumerable) + { + if (elm == 5) break; + // .. + Console.WriteLine(elm); + } + + } + + #endregion + + static void WaitAndPress() + { + Console.WriteLine("Appuyer sur une touche pour continuer..."); + Console.ReadKey(); + Console.WriteLine(); + } static void Main(string[] args) { - Console.WriteLine("* Exemple ForEach"); + #region Exemple boucles + Console.WriteLine("* Boucle ForEach"); ForEach(); Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Boucle ForEach avec un Disposable"); + ForEachDisposable(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Itération de base (simulation ForEach)"); + IterationBase(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Itération de base avec un disposable (simulation ForEach)"); + IterationBaseWithDispose(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Boucle ForEach avec un objet non IEnumerable"); + ForEachWithoutIEnumerable(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Test ReverseEnumerator"); + TestReverse(); + Console.WriteLine(); + WaitAndPress(); + #endregion - Console.ReadKey(); } } } diff --git a/Enumerables-Technet/Source/Enumerables-fr/ReverseEnumerable.cs b/Enumerables-Technet/Source/Enumerables-fr/ReverseEnumerable.cs new file mode 100644 index 0000000..3d7a90b --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/ReverseEnumerable.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Implémentation de IEnumerable permettant de parcourir une liste dans le sens inverse + /// + public class ReverseEnumerable : IEnumerable + { + + /// + /// Nouvelle classe énumérable + /// + public ReverseEnumerable(IList source) + { + this.List = source; + } + + /// + /// Implémentation de IEnumerable<T>.GetEnumerator() + /// + /// Créé un nouveau ReverseEnumerator + public IEnumerator GetEnumerator() + { + return new ReverseEnumerator(List); + } + + /// + /// IEnumerable.GetEnumerator() (version non générique) + /// + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Liste source + /// + public IList List { get; private set; } + + } +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/ReverseEnumerator.cs b/Enumerables-Technet/Source/Enumerables-fr/ReverseEnumerator.cs new file mode 100644 index 0000000..1d970d6 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/ReverseEnumerator.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + + /// + /// Enumérateur parcourant une liste dans le sens inverse + /// + public class ReverseEnumerator : IEnumerator + { + IList _Source; + int _Position; + bool _Completed; + + /// + /// Création d'un nouvel énumérateur + /// + public ReverseEnumerator(IList source) + { + this._Source = source; + // On met -1 pour indiquer qu'on a pas commencé l'itération + this._Position = -1; + // L'itération n'est pas terminée + this._Completed = false; + // On défini Current avec la valeur par défaut + this.Current = default(T); + } + + /// + /// Libération des ressources + /// + public void Dispose() + { + // On a rien à libérer , mais on marque notre itérateur comme terminé + this._Completed = true; + Console.WriteLine("Enumerateur disposé"); + } + + /// + /// Cette méthode est appelée lorsque l'on veut réinitialiser l'énumérateur + /// + public void Reset() + { + // On met -1 pour indiquer qu'on a pas commencer l'itération + this._Position = -1; + // L'itération n'est pas terminée + this._Completed = false; + // On défini Current avec la valeur par défaut + this.Current = default(T); + } + + /// + /// On se déplace vers le prochain élément + /// + /// False lorsque l'itération est terminée + public bool MoveNext() + { + // Si la source est Null alors on a rien à parcourir, donc l'itération s'arrête + if (this._Source == null) return false; + + // Si l'itération est terminée alors on ne va pas plus loin + if (this._Completed) return false; + + // Si la position est à -1 on récupère le nombre d'éléments à parcourir pour démarrer l'itération + if (this._Position == -1) + { + Console.WriteLine("Itération commencée"); + this._Position = _Source.Count; + } + + // On se déplace dans la liste + this._Position--; + + // Si on a atteind -1 alors on a terminé l'itération + if (this._Position < 0) + { + this._Completed = true; + Console.WriteLine("Itération terminée"); + return false; + } + + // On défini Current et on continue + Current = this._Source[this._Position]; + + return true; + } + + /// + /// Elément en cours de l'itération + /// + public T Current { get; private set; } + + /// + /// Elément en cours pour la version non générique + /// + object System.Collections.IEnumerator.Current + { + get { return Current; } + } + + } + +} From 4f24d8eb49679822942ece9111c3a91292e122be Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Wed, 24 Jun 2015 21:20:30 +0200 Subject: [PATCH 04/11] =?UTF-8?q?Enum=C3=A9rateurs=20complexe=20de=20fichi?= =?UTF-8?q?ers.=20M=C3=A9thodes=20yield.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Enumerables-fr/EnumFilesV1.cs | 90 +++++++ .../Source/Enumerables-fr/EnumFilesV2.cs | 84 ++++++ .../Source/Enumerables-fr/EnumFilesV3.cs | 209 ++++++++++++++ .../Enumerables-fr/Enumerables-fr.csproj | 14 + .../Source/Enumerables-fr/Files/Fichier1.txt | 3 + .../Source/Enumerables-fr/Files/Fichier2.txt | 3 + .../Source/Enumerables-fr/Files/Fichier3.txt | 3 + .../Source/Enumerables-fr/Program.cs | 255 ++++++++++++++++++ Enumerables-Technet/Source/Enumerables.sln | 12 - 9 files changed, 661 insertions(+), 12 deletions(-) create mode 100644 Enumerables-Technet/Source/Enumerables-fr/EnumFilesV1.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/EnumFilesV2.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/EnumFilesV3.cs create mode 100644 Enumerables-Technet/Source/Enumerables-fr/Files/Fichier1.txt create mode 100644 Enumerables-Technet/Source/Enumerables-fr/Files/Fichier2.txt create mode 100644 Enumerables-Technet/Source/Enumerables-fr/Files/Fichier3.txt diff --git a/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV1.cs b/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV1.cs new file mode 100644 index 0000000..9061393 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV1.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Enumérable parcourant les lignes texte d'un ensemble de fichier + /// + public class EnumFilesV1 : IEnumerable + { + + private List _Lines; + + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV1(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + + // On marque la liste des lignes à charger en la mettant à null + _Lines = null; + } + + void LoadFiles() + { + // Création de la liste des lignes + _Lines = new List(); + + if (this.Files != null) + { + // Pour chaque fichier + foreach (var file in Files) + { + try + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + _Lines.Add(line); + } + } + } + catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier + } + } + } + + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // Si la liste des lignes est null alors il faut lire les fichiers + if (_Lines == null) + { + // Chargement des fichiers + LoadFiles(); + } + + // Retourne l'énumérateur de la liste + return _Lines.GetEnumerator(); + } + + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV2.cs b/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV2.cs new file mode 100644 index 0000000..5702183 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV2.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Enumérable parcourant les lignes texte d'un ensemble de fichier reconstruisant une liste à chaque appel + /// + public class EnumFilesV2 : IEnumerable + { + + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV2(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + } + + IList LoadFiles() + { + // Création de la liste des lignes + var result = new List(); + + if (this.Files != null) + { + // Pour chaque fichier + foreach (var file in Files) + { + try + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + result.Add(line); + } + } + } + catch { } // Si une erreur à lecture du fichier on passe au prochain fichier + } + } + + // On retourne la liste + return result; + } + + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // On construit la liste + var list = LoadFiles(); + + // Retourne l'énumérateur de la liste + return list.GetEnumerator(); + } + + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV3.cs b/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV3.cs new file mode 100644 index 0000000..ea98919 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-fr/EnumFilesV3.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Enumérable parcourant les lignes texte d'un ensemble de fichier via un énumérateur + /// + public class EnumFilesV3 : IEnumerable + { + + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV3(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + } + + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // Retourne un nouvel énumérateur + return new FileEnumerator(Files); + } + + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + + } + + /// + /// Enumérateur de fichier + /// + class FileEnumerator : IEnumerator + { + Func _CurrentState = null; + int _CurrentFilePos; + String _CurrentFileName; + TextReader _CurrentFile; + + /// + /// Création d'un nouvel énumérateur + /// + public FileEnumerator(String[] files) + { + // Initialisation des fichiers + this.Files = files; + // Initialisation de l'énumérateur + Current = null; + _CurrentFilePos = 0; + _CurrentFileName = null; + _CurrentFile = null; + // L'état de l'énumérateur est à l'ouverture du prochain fichier à traiter + _CurrentState = OpenNextFileState; + } + + /// + /// Libération des ressources éventuelles + /// + public void Dispose() + { + // Si on a un fichier encore d'ouvert on libère la mémoire + if (_CurrentFile != null) + { + _CurrentFile.Dispose(); + _CurrentFile = null; + } + // On défini l'état 'Completed' + _CurrentState = CompletedState; + } + + /// + /// Essayes d'ouvrir le prochain fichier de la liste + /// + bool GetNextFile() + { + String filename = null; + TextReader file = null; + while (file == null && Files != null && _CurrentFilePos < Files.Length) + { + try + { + filename = Files[_CurrentFilePos++]; + file = new StreamReader(filename); + } + catch { } + } + // Si on a un fichier d'ouvert + if (file != null) + { + _CurrentFileName = filename; + _CurrentFile = file; + return true; + } + // Sinon on a rien trouvé + return false; + } + + /// + /// Ouverture du prochain fichier + /// + bool OpenNextFileState() + { + // Si on a pas ou plus de fichier on arrête tout + if (!GetNextFile()) + { + Current = null; + // On passe à l'état 'Completed' + _CurrentState = CompletedState; + // On termine + return false; + } + + // On passe à l'état ReadNextLine + _CurrentState = ReadNextLineState; + + // On lit la première ligne + return _CurrentState(); + } + + /// + /// On est en cours de lecture + /// + bool ReadNextLineState() + { + try + { + // On lit la prochaine ligne du fichier + String line = _CurrentFile.ReadLine(); + // Si la ligne n'est pas null on la traite + if (line != null) + { + Current = line; + return true; + } + // La ligne est null alors on a atteint la fin du fichier, on libère sa ressource + } + catch + { + // Si une erreur survient à la lecture on ferme le fichier en cours pour éviter les boucles infinies + + } + // Libération des ressources pour passer au fichier suivant + _CurrentFile.Dispose(); + _CurrentFile = null; + _CurrentFileName = null; + // On passe à l'état 'OpenNextFile' + _CurrentState = OpenNextFileState; + // On traite le prochain état + return _CurrentState(); + } + + /// + /// L'itération est terminée on retourne toujours false + /// + bool CompletedState() + { + return false; + } + + /// + /// On ne s'occupe pas de cette méthode + /// + public void Reset() + { + throw new NotSupportedException(); + } + + /// + /// On se déplace + /// + public bool MoveNext() + { + // Exécution de l'état en cours + return _CurrentState(); + } + + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + + /// + /// Valeur en cours + /// + public string Current { get; private set; } + object System.Collections.IEnumerator.Current { get { return Current; } } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj b/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj index f5915ed..1ae0984 100644 --- a/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj +++ b/Enumerables-Technet/Source/Enumerables-fr/Enumerables-fr.csproj @@ -44,6 +44,9 @@ + + + @@ -54,6 +57,17 @@ + + + Always + + + Always + + + Always + + - \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart1/Program.cs b/Enumerables-Technet/Source/PrgPart1/Program.cs deleted file mode 100644 index 731a901..0000000 --- a/Enumerables-Technet/Source/PrgPart1/Program.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart1 -{ - class Program - { - /// - /// Retourne une liste d'éléments - /// - static int[] GetItems() - { - return new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - } - - /// - /// Exemple foreach - /// - static void ForEach() - { - // Récupération de l'énumérable - IEnumerable enumerable = GetItems(); - - // Parcours chaque élément dans elm - foreach (int elm in enumerable) - { - // .. - Console.WriteLine(elm); - } - } - - /// - /// Exemple d'itération de base - /// - static void IterationBase() - { - // Récupération de l'énumérable - IEnumerable enumerable = GetItems(); - - // Récupère un nouvel énumérateur - IEnumerator enumerator = enumerable.GetEnumerator(); - // Tant que l'énumérateur se déplace - while (enumerator.MoveNext()) - { - Int32 elm = enumerator.Current; - // .. - Console.WriteLine(elm); - } - } - - /// - /// Exemple d'itération avec gestion du IDisposable tel que le fait un foreach - /// - static void IterationBaseWithDispose() - { - // Récupération de l'énumérable - IEnumerable enumerable = GetItems(); - - // Récupère un nouvel énumérateur - IEnumerator enumerator = enumerable.GetEnumerator(); - try - { - // Tant que l'énumérateur se déplace - while (enumerator.MoveNext()) - { - Int32 elm = enumerator.Current; - // .. - Console.WriteLine(elm); - } - } - finally - { - // On détermine si l'énumérateur est disposable - IDisposable disp = enumerator as IDisposable; - // Si c'est le cas on dispose l'énumérateur - if (disp != null) - { - disp.Dispose(); - } - } - } - - /// - /// Test ReverseEnumerator - /// - static void TestReverse() - { - // ReverseEnumerator attend un IList<>, aussi on récupère le tableau comme IList. - // Tous les tableaux .Net sont des IList<> donc il n'y a pas de problème de cast - IList list = GetItems(); - - // Création de l'énumerable reverse - IEnumerable enumerable = new ReverseEnumerable(list); - - // Parcours chaque élément dans elm - foreach (int elm in enumerable) - { - // .. - Console.WriteLine(elm); - } - - // On parcours chaque élément mais en arrêtant la boucle quand on trouve l'élément 5 - foreach (int elm in enumerable) - { - if (elm == 5) break; - // .. - Console.WriteLine(elm); - } - - } - - static void Main(string[] args) - { - //ForEach(); - //IterationBase(); - //IterationBaseWithDispose(); - TestReverse(); - - Console.ReadLine(); - } - - } -} diff --git a/Enumerables-Technet/Source/PrgPart1/Properties/AssemblyInfo.cs b/Enumerables-Technet/Source/PrgPart1/Properties/AssemblyInfo.cs deleted file mode 100644 index e238176..0000000 --- a/Enumerables-Technet/Source/PrgPart1/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Les informations générales relatives à un assembly dépendent de -// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations -// associées à un assembly. -[assembly: AssemblyTitle("PrgPart1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("PrgPart1")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly -// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de -// COM, affectez la valeur true à l'attribut ComVisible sur ce type. -[assembly: ComVisible(false)] - -// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM -[assembly: Guid("b0b96a67-03b4-469d-8a73-2cf41fe62c9c")] - -// Les informations de version pour un assembly se composent des quatre valeurs suivantes : -// -// Version principale -// Version secondaire -// Numéro de build -// Révision -// -// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut -// en utilisant '*', comme indiqué ci-dessous : -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Enumerables-Technet/Source/PrgPart1/ReverseEnumerable.cs b/Enumerables-Technet/Source/PrgPart1/ReverseEnumerable.cs deleted file mode 100644 index ce619e5..0000000 --- a/Enumerables-Technet/Source/PrgPart1/ReverseEnumerable.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart1 -{ - /// - /// Implémentation de IEnumerable permettant de parcourir une liste dans le sens inverse - /// - public class ReverseEnumerable : IEnumerable - { - - /// - /// Nouvelle classe énumérable - /// - public ReverseEnumerable(IList source) - { - this.List = source; - } - - /// - /// Implémentation de IEnumerable<T>.GetEnumerator() - /// - /// Créé un nouveau ReverseEnumerator - public IEnumerator GetEnumerator() - { - return new ReverseEnumerator(List); - } - - /// - /// IEnumerable.GetEnumerator() (version non générique) - /// - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Liste source - /// - public IList List { get; private set; } - - } -} diff --git a/Enumerables-Technet/Source/PrgPart1/ReverseEnumerator.cs b/Enumerables-Technet/Source/PrgPart1/ReverseEnumerator.cs deleted file mode 100644 index d036846..0000000 --- a/Enumerables-Technet/Source/PrgPart1/ReverseEnumerator.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart1 -{ - - /// - /// Enumérateur parcourant une liste dans le sens inverse - /// - public class ReverseEnumerator : IEnumerator - { - IList _Source; - int _Position; - bool _Completed; - - /// - /// Création d'un nouvel énumérateur - /// - public ReverseEnumerator(IList source) - { - this._Source = source; - // On met -1 pour indiquer qu'on a pas commencé l'itération - this._Position = -1; - // L'itération n'est pas terminée - this._Completed = false; - // On défini Current avec la valeur par défaut - this.Current = default(T); - } - - /// - /// Libération des ressources - /// - public void Dispose() - { - // On a rien à libérer , mais on marque notre itérateur comme terminé - this._Completed = true; - Console.WriteLine("Enumerateur disposé"); - } - - /// - /// Cette méthode est appelée lorsque l'on veut réinitialiser l'énumérateur - /// - public void Reset() - { - // On met -1 pour indiquer qu'on a pas commencer l'itération - this._Position = -1; - // L'itération n'est pas terminée - this._Completed = false; - // On défini Current avec la valeur par défaut - this.Current = default(T); - } - - /// - /// On se déplace vers le prochain élément - /// - /// False lorsque l'itération est terminée - public bool MoveNext() - { - // Si la source est Null alors on a rien à parcourir, donc l'itération s'arrête - if (this._Source == null) return false; - - // Si l'itération est terminée alors on ne va pas plus loin - if (this._Completed) return false; - - // Si la position est à -1 on récupère le nombre d'éléments à parcourir pour démarrer l'itération - if (this._Position == -1) - { - Console.WriteLine("Itération commencée"); - this._Position = _Source.Count; - } - - // On se déplace dans la liste - this._Position--; - - // Si on a atteind -1 alors on a terminé l'itération - if (this._Position < 0) - { - this._Completed = true; - Console.WriteLine("Itération terminée"); - return false; - } - - // On défini Current et on continue - Current = this._Source[this._Position]; - - return true; - } - - /// - /// Elément en cours de l'itération - /// - public T Current { get; private set; } - - /// - /// Elément en cours pour la version non générique - /// - object System.Collections.IEnumerator.Current - { - get { return Current; } - } - - } - -} diff --git a/Enumerables-Technet/Source/PrgPart2/App.config b/Enumerables-Technet/Source/PrgPart2/App.config deleted file mode 100644 index 9c05822..0000000 --- a/Enumerables-Technet/Source/PrgPart2/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart2/EnumFichierBarbare.cs b/Enumerables-Technet/Source/PrgPart2/EnumFichierBarbare.cs deleted file mode 100644 index 2ade2ea..0000000 --- a/Enumerables-Technet/Source/PrgPart2/EnumFichierBarbare.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart2 -{ - /// - /// Enumérable parcourant les lignes texte d'un ensemble de fichier - /// - public class EnumFichierBarbare : IEnumerable - { - - private List _Lines; - - /// - /// Création d'un nouvel énumérable - /// - public EnumFichierBarbare(String[] files) - { - // Initialisation des fichiers - this.Files = files; - - // On marque la liste des lignes à charger en la mettant à null - _Lines = null; - } - - void LoadFiles() - { - // Création de la liste des lignes - _Lines = new List(); - - if (this.Files != null) - { - // Pour chaque fichier - foreach (var file in Files) - { - try - { - // Ouverture d'un lecteur de fichier texte - using (var reader = new StreamReader(file)) - { - // Lecture de chaque ligne du fichier - String line; - while ((line = reader.ReadLine()) != null) - { - // On ajoute la ligne dans la liste - _Lines.Add(line); - } - } - } - catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier - } - } - } - - /// - /// Retourne l'énumérateur des lignes - /// - public IEnumerator GetEnumerator() - { - // Si la liste des lignes est null alors il faut lire les fichiers - if (_Lines == null) - { - // Chargement des fichiers - LoadFiles(); - } - - // Retourne l'énumérateur de la liste - return _Lines.GetEnumerator(); - } - - /// - /// Implémentation de IEnumerator.GetEnumerator() (version non générique) - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Liste des fichiers - /// - public String[] Files { get; private set; } - - } - -} diff --git a/Enumerables-Technet/Source/PrgPart2/EnumFichierSubtile.cs b/Enumerables-Technet/Source/PrgPart2/EnumFichierSubtile.cs deleted file mode 100644 index 7f43275..0000000 --- a/Enumerables-Technet/Source/PrgPart2/EnumFichierSubtile.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart2 -{ - /// - /// Enumérable parcourant les lignes texte d'un ensemble de fichier via un énumérateur - /// - public class EnumFichierSubtile : IEnumerable - { - - /// - /// Création d'un nouvel énumérable - /// - public EnumFichierSubtile(String[] files) - { - // Initialisation des fichiers - this.Files = files; - } - - /// - /// Retourne l'énumérateur des lignes - /// - public IEnumerator GetEnumerator() - { - // Retourne un nouvel énumérateur - return new FichierEnumerator(Files); - } - - /// - /// Implémentation de IEnumerator.GetEnumerator() (version non générique) - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Liste des fichiers - /// - public String[] Files { get; private set; } - - } - - /// - /// Enumérateur de fichier - /// - class FichierEnumerator : IEnumerator - { - Func _CurrentState = null; - int _CurrentFilePos; - String _CurrentFileName; - TextReader _CurrentFile; - - /// - /// Création d'un nouvel énumérateur - /// - public FichierEnumerator(String[] files) - { - // Initialisation des fichiers - this.Files = files; - // Initialisation de l'énumérateur - Current = null; - _CurrentFilePos = 0; - _CurrentFileName = null; - _CurrentFile = null; - // L'état de l'énumérateur est à l'ouverture du prochain fichier à traiter - _CurrentState = OpenNextFileState; - } - - /// - /// Libération des ressources éventuelles - /// - public void Dispose() - { - // Si on a un fichier encore d'ouvert on libère la mémoire - if (_CurrentFile != null) - { - _CurrentFile.Dispose(); - _CurrentFile = null; - } - // On défini l'état 'Completed' - _CurrentState = CompletedState; - } - - /// - /// Essayes d'ouvrir le prochain fichier de la liste - /// - bool GetNextFile() - { - String filename = null; - TextReader file = null; - while (file == null && Files != null && _CurrentFilePos < Files.Length) - { - try - { - filename = Files[_CurrentFilePos++]; - file = new StreamReader(filename); - } - catch { } - } - // Si on a un fichier d'ouvert - if (file != null) - { - _CurrentFileName = filename; - _CurrentFile = file; - return true; - } - // Sinon on a rien trouvé - return false; - } - - /// - /// Ouverture du prochain fichier - /// - bool OpenNextFileState() - { - // Si on a pas ou plus de fichier on arrête tout - if (!GetNextFile()) - { - Current = null; - // On passe à l'état 'Completed' - _CurrentState = CompletedState; - // On termine - return false; - } - - // On passe à l'état ReadNextLine - _CurrentState = ReadNextLineState; - - // On lit la première ligne - return _CurrentState(); - } - - /// - /// On est en cours de lecture - /// - bool ReadNextLineState() - { - try - { - // On lit la prochaine ligne du fichier - String line = _CurrentFile.ReadLine(); - // Si la ligne n'est pas null on la traite - if (line != null) - { - Current = line; - return true; - } - // La ligne est null alors on a atteint la fin du fichier, on libère sa ressource - } - catch - { - // Si une erreur survient à la lecture on ferme le fichier en cours pour éviter les boucles infinies - - } - // Libération des ressources pour passer au fichier suivant - _CurrentFile.Dispose(); - _CurrentFile = null; - _CurrentFileName = null; - // On passe à l'état 'OpenNextFile' - _CurrentState = OpenNextFileState; - // On traite le prochain état - return _CurrentState(); - } - - /// - /// L'itération est terminée on retourne toujours false - /// - bool CompletedState() - { - return false; - } - - /// - /// On ne s'occupe pas de cette méthode - /// - public void Reset() - { - throw new NotSupportedException(); - } - - /// - /// On se déplace - /// - public bool MoveNext() - { - // Exécution de l'état en cours - return _CurrentState(); - } - - /// - /// Liste des fichiers - /// - public String[] Files { get; private set; } - - /// - /// Valeur en cours - /// - public string Current { get; private set; } - object System.Collections.IEnumerator.Current { get { return Current; } } - - } - -} diff --git a/Enumerables-Technet/Source/PrgPart2/EnumFichierUnPeuMoinsBarbare.cs b/Enumerables-Technet/Source/PrgPart2/EnumFichierUnPeuMoinsBarbare.cs deleted file mode 100644 index d7ac9bd..0000000 --- a/Enumerables-Technet/Source/PrgPart2/EnumFichierUnPeuMoinsBarbare.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart2 -{ - /// - /// Enumérable parcourant les lignes texte d'un ensemble de fichier reconstruisant une liste à chaque appel - /// - public class EnumFichierUnPeuMoinsBarbare : IEnumerable - { - - /// - /// Création d'un nouvel énumérable - /// - public EnumFichierUnPeuMoinsBarbare(String[] files) - { - // Initialisation des fichiers - this.Files = files; - } - - IList LoadFiles() - { - // Création de la liste des lignes - var result = new List(); - - if (this.Files != null) - { - // Pour chaque fichier - foreach (var file in Files) - { - try - { - // Ouverture d'un lecteur de fichier texte - using (var reader = new StreamReader(file)) - { - // Lecture de chaque ligne du fichier - String line; - while ((line = reader.ReadLine()) != null) - { - // On ajoute la ligne dans la liste - result.Add(line); - } - } - } - catch { } // Si une erreur à lecture du fichier on passe au prochain fichier - } - } - - // On retourne la liste - return result; - } - - /// - /// Retourne l'énumérateur des lignes - /// - public IEnumerator GetEnumerator() - { - // On construit la liste - var list = LoadFiles(); - - // Retourne l'énumérateur de la liste - return list.GetEnumerator(); - } - - /// - /// Implémentation de IEnumerator.GetEnumerator() (version non générique) - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Liste des fichiers - /// - public String[] Files { get; private set; } - - } - -} diff --git a/Enumerables-Technet/Source/PrgPart2/Files/Fichier1.txt b/Enumerables-Technet/Source/PrgPart2/Files/Fichier1.txt deleted file mode 100644 index 6241aba..0000000 --- a/Enumerables-Technet/Source/PrgPart2/Files/Fichier1.txt +++ /dev/null @@ -1,3 +0,0 @@ -Première ligne du premier fichier. -Deuxième ligne du premier fichier. -Troisième ligne du premier fichier. diff --git a/Enumerables-Technet/Source/PrgPart2/Files/Fichier2.txt b/Enumerables-Technet/Source/PrgPart2/Files/Fichier2.txt deleted file mode 100644 index 6cf2956..0000000 --- a/Enumerables-Technet/Source/PrgPart2/Files/Fichier2.txt +++ /dev/null @@ -1,3 +0,0 @@ -Première ligne du second fichier. -Deuxième ligne du second fichier. -Troisième ligne du second fichier. diff --git a/Enumerables-Technet/Source/PrgPart2/Files/Fichier3.txt b/Enumerables-Technet/Source/PrgPart2/Files/Fichier3.txt deleted file mode 100644 index f984105..0000000 --- a/Enumerables-Technet/Source/PrgPart2/Files/Fichier3.txt +++ /dev/null @@ -1,3 +0,0 @@ -Première ligne du troisième fichier. -Deuxième ligne du troisième fichier. -Troisième ligne du troisième fichier. diff --git a/Enumerables-Technet/Source/PrgPart2/PrgPart2.csproj b/Enumerables-Technet/Source/PrgPart2/PrgPart2.csproj deleted file mode 100644 index ece0eb5..0000000 --- a/Enumerables-Technet/Source/PrgPart2/PrgPart2.csproj +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Debug - AnyCPU - {813646C0-C8E7-4EC7-AD63-B021AE490099} - Exe - Properties - PrgPart2 - PrgPart2 - v4.5.1 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - Always - - - Always - - - Always - - - - - \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart2/Program.cs b/Enumerables-Technet/Source/PrgPart2/Program.cs deleted file mode 100644 index 3495b10..0000000 --- a/Enumerables-Technet/Source/PrgPart2/Program.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart2 -{ - class Program - { - - static String[] GetTestFileNames() - { - var dir = AppDomain.CurrentDomain.BaseDirectory; - return new String[]{ - Path.Combine(dir, "Files", "Fichier1.txt"), - Path.Combine(dir, "Files", "Fichier2.txt"), - Path.Combine(dir, "Files", "Fichier3.txt") - }; - } - - private static void TestFichierBarbare() - { - // Création de l'énumérable avec les fichiers de tests - var enumerable = new EnumFichierBarbare(GetTestFileNames()); - - // On parcours l'énumérable - foreach (var line in enumerable) - { - Console.WriteLine(line); - } - - } - - private static void TestFichierUnPeuMoinsBarbare() - { - // Création de l'énumérable avec les fichiers de tests - var enumerable = new EnumFichierUnPeuMoinsBarbare(GetTestFileNames()); - - // On parcours l'énumérable - foreach (var line in enumerable) - { - Console.WriteLine(line); - } - - } - - private static void TestFichierSubtile() - { - // Création de l'énumérable avec les fichiers de tests - var enumerable = new EnumFichierSubtile(GetTestFileNames()); - - // On parcours l'énumérable - foreach (var line in enumerable) - { - Console.WriteLine(line); - } - - // On parcours l'énumérable et provoque un arrêt prématuré - int i = 0; - foreach (var line in enumerable) - { - if (i++ >= 4) break; - Console.WriteLine(line); - } - - } - - /// - /// Ouvre un nouveau fichier ou retourne null si une erreur à lieu - /// - static StreamReader OpenFile(String file) - { - try - { - return new StreamReader(file); - } - catch - { - return null; - } - } - - static IEnumerable EnumFichierYield(String[] files) - { - if (files != null) - { - // Pour chaque fichier - foreach (var file in files) - { - // Ouverture d'un lecteur de fichier texte - using (var reader = OpenFile(file)) - { - // reader peut être null si une erreur à eu lieu - if (reader != null) - { - // Lecture de chaque ligne du fichier - String line; - do - { - // Lecture d'une ligne, si une erreur à lieu on arrête la boucle - try - { - line = reader.ReadLine(); - } - catch - { - break; - } - // On envoi la ligne d'énumérable - if (line != null) - yield return line; - } while (line != null);// Boucle tant qu'on a une ligne - } - } - } - } - } - - static IEnumerable EnumFichierYieldUnprotected(String[] files) - { - if (files != null) - { - // Pour chaque fichier - foreach (var file in files) - { - // Ouverture d'un lecteur de fichier texte - using (var reader = new StreamReader(file)) - { - // Lecture de chaque ligne du fichier - String line; - while ((line = reader.ReadLine()) != null) - { - // On ajoute la ligne dans la liste - yield return line; - } - } - } - } - } - - private static void TestFichierYield() - { - // Récupération d'un 'énumérable avec les fichiers de tests - var enumerable = EnumFichierYield(GetTestFileNames()); - - // On parcours l'énumérable - foreach (var line in enumerable) - { - Console.WriteLine(line); - } - - // On parcours l'énumérable et provoque un arrêt prématuré - int i = 0; - foreach (var line in enumerable) - { - if (i++ >= 4) break; - Console.WriteLine(line); - } - - } - - static IEnumerable EnumComplex() - { - Random rnd = new Random(); - - // Emission directe - yield return "Start"; - - // Emission dans un switch - for (int i = 0; i < 10; i++) - { - switch (rnd.Next(4)) - { - case 1: - yield return "Funky 1"; - break; - case 2: - continue; - case 3: - yield return "<<<<"; - // Emission d'un autre énum - foreach (var line in EnumFichierYield(GetTestFileNames())) - { - // On place une condition - if (line.Contains("x")) - yield return line; - } - yield return ">>>>"; - break; - case 0: - default: - yield return "Funky 0"; - break; - } - } - - // Emission directe - yield return "End"; - } - - static void TestEnumComplex() - { - foreach (var item in EnumComplex()) - Console.WriteLine(item); - } - - static void Main(string[] args) - { - //TestFichierBarbare(); - //TestFichierUnPeuMoinsBarbare(); - //TestFichierSubtile(); - //TestFichierYield(); - TestEnumComplex(); - - Console.ReadLine(); - } - - } -} diff --git a/Enumerables-Technet/Source/PrgPart2/Properties/AssemblyInfo.cs b/Enumerables-Technet/Source/PrgPart2/Properties/AssemblyInfo.cs deleted file mode 100644 index a55a358..0000000 --- a/Enumerables-Technet/Source/PrgPart2/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Les informations générales relatives à un assembly dépendent de -// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations -// associées à un assembly. -[assembly: AssemblyTitle("PrgPart2")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("PrgPart2")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly -// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de -// COM, affectez la valeur true à l'attribut ComVisible sur ce type. -[assembly: ComVisible(false)] - -// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM -[assembly: Guid("3e47d205-0d3d-4ea8-9fbd-cb6565cce4db")] - -// Les informations de version pour un assembly se composent des quatre valeurs suivantes : -// -// Version principale -// Version secondaire -// Numéro de build -// Révision -// -// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut -// en utilisant '*', comme indiqué ci-dessous : -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Enumerables-Technet/Source/PrgPart3/App.config b/Enumerables-Technet/Source/PrgPart3/App.config deleted file mode 100644 index 9c05822..0000000 --- a/Enumerables-Technet/Source/PrgPart3/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart3/Person.cs b/Enumerables-Technet/Source/PrgPart3/Person.cs deleted file mode 100644 index 4d7966f..0000000 --- a/Enumerables-Technet/Source/PrgPart3/Person.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart3 -{ - class Person - { - public String Name { get; set; } - public DateTime? BirthDay { get; set; } - public bool IsFamily { get; set; } - } -} diff --git a/Enumerables-Technet/Source/PrgPart3/PrgPart3.csproj b/Enumerables-Technet/Source/PrgPart3/PrgPart3.csproj deleted file mode 100644 index ab39d74..0000000 --- a/Enumerables-Technet/Source/PrgPart3/PrgPart3.csproj +++ /dev/null @@ -1,60 +0,0 @@ - - - - - Debug - AnyCPU - {41349392-BB15-4575-9B6D-D3B734096B90} - Exe - Properties - PrgPart3 - PrgPart3 - v4.5.1 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Enumerables-Technet/Source/PrgPart3/Program.cs b/Enumerables-Technet/Source/PrgPart3/Program.cs deleted file mode 100644 index 1c47689..0000000 --- a/Enumerables-Technet/Source/PrgPart3/Program.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace PrgPart3 -{ - class Program - { - static IEnumerable GetPersons() - { - yield return new Person() { - Name = "Toto", - BirthDay = null, - IsFamily = true - }; - yield return new Person() { - Name = "Titi", - BirthDay = new DateTime(2010, 11, 27), - IsFamily = false - }; - yield return new Person() { - Name = "Tata", - BirthDay = null, - IsFamily = false - }; - yield return new Person() { - Name = "Tutu", - BirthDay = new DateTime(1994, 8, 11), - IsFamily = true - }; - } - - static void TestLinqRequest() - { - var query = from p in GetPersons() - where p.IsFamily - orderby p.Name - select p; - - foreach (var p in query) - Console.WriteLine(p.Name); - - foreach (var p in query.Take(2)) - Console.WriteLine(p.Name); - } - - static void TestLinqRequestAsFluent() - { - var query = GetPersons() - .Where(p => p.IsFamily) - .OrderBy(p => p.Name) - ; - - foreach (var p in query) - Console.WriteLine(p.Name); - - foreach (var p in query.Take(2)) - Console.WriteLine(p.Name); - } - - //static void TestStats() - //{ - // var q = from stats in GetStats() - // where stat.Date >= lastRefresh - // orderby stat.Date - // select stats; - - // foreach (var stats in q) - // { - // // Affichage de la stat - // } - - // lblLastUpdate.Text = String.Format("{0} : {1} nouvelles statistiques", DateTime.Now, q.Count()); - // lastRefresh = DateTime.Now; - - //} - - static IEnumerable SimulWhere() - { - foreach (var p in GetPersons()) - { - if(p.IsFamily) - yield return p; - } - } - - static IEnumerable SimulOrderBy() - { - List result = new List(); - result.AddRange(SimulWhere()); - result.Sort((x, y) => String.Compare(x.Name, y.Name)); - foreach (var p in result) - yield return p; - } - - static void TestSimulLinq() - { - var query = SimulOrderBy(); - - foreach (var p in query) - Console.WriteLine(p.Name); - - foreach (var p in query.Take(2)) - Console.WriteLine(p.Name); - } - - static void Main(string[] args) - { - //TestLinqRequest(); - //TestLinqRequestAsFluent(); - TestSimulLinq(); - - Console.ReadLine(); - } - } -} diff --git a/Enumerables-Technet/Source/PrgPart3/Properties/AssemblyInfo.cs b/Enumerables-Technet/Source/PrgPart3/Properties/AssemblyInfo.cs deleted file mode 100644 index 43f64c6..0000000 --- a/Enumerables-Technet/Source/PrgPart3/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Les informations générales relatives à un assembly dépendent de -// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations -// associées à un assembly. -[assembly: AssemblyTitle("PrgPart3")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("PrgPart3")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly -// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de -// COM, affectez la valeur true à l'attribut ComVisible sur ce type. -[assembly: ComVisible(false)] - -// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM -[assembly: Guid("5406360f-dd7b-4149-94c4-3bba295c3a8d")] - -// Les informations de version pour un assembly se composent des quatre valeurs suivantes : -// -// Version principale -// Version secondaire -// Numéro de build -// Révision -// -// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut -// en utilisant '*', comme indiqué ci-dessous : -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] From 17132c1299b00204c801f99814e67d6686f5dc80 Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Sat, 27 Jun 2015 08:36:58 +0200 Subject: [PATCH 07/11] Version anglaise --- .../Source/Enumerables-en/App.config | 6 + .../Source/Enumerables-en/EnumFilesV1.cs | 90 +++ .../Source/Enumerables-en/EnumFilesV2.cs | 84 +++ .../Source/Enumerables-en/EnumFilesV3.cs | 209 +++++++ .../EnumerableWithEnumeratorDisposable.cs | 46 ++ .../Enumerables-en/Enumerables-en.csproj | 80 +++ .../Enumerables-en/EnumeratorDisposable.cs | 61 ++ .../Source/Enumerables-en/FakeEnumerable.cs | 24 + .../Source/Enumerables-en/FakeEnumerator.cs | 32 + .../Source/Enumerables-en/Files/Fichier1.txt | 3 + .../Source/Enumerables-en/Files/Fichier2.txt | 3 + .../Source/Enumerables-en/Files/Fichier3.txt | 3 + .../Source/Enumerables-en/Person.cs | 15 + .../Source/Enumerables-en/Program.cs | 586 ++++++++++++++++++ .../Enumerables-en/Properties/AssemblyInfo.cs | 36 ++ .../Enumerables-en/ReverseEnumerable.cs | 47 ++ .../Enumerables-en/ReverseEnumerator.cs | 107 ++++ Enumerables-Technet/Source/Enumerables.sln | 6 + 18 files changed, 1438 insertions(+) create mode 100644 Enumerables-Technet/Source/Enumerables-en/App.config create mode 100644 Enumerables-Technet/Source/Enumerables-en/EnumFilesV1.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/EnumFilesV2.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/EnumFilesV3.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/EnumerableWithEnumeratorDisposable.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/Enumerables-en.csproj create mode 100644 Enumerables-Technet/Source/Enumerables-en/EnumeratorDisposable.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/FakeEnumerable.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/FakeEnumerator.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/Files/Fichier1.txt create mode 100644 Enumerables-Technet/Source/Enumerables-en/Files/Fichier2.txt create mode 100644 Enumerables-Technet/Source/Enumerables-en/Files/Fichier3.txt create mode 100644 Enumerables-Technet/Source/Enumerables-en/Person.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/Program.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/Properties/AssemblyInfo.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/ReverseEnumerable.cs create mode 100644 Enumerables-Technet/Source/Enumerables-en/ReverseEnumerator.cs diff --git a/Enumerables-Technet/Source/Enumerables-en/App.config b/Enumerables-Technet/Source/Enumerables-en/App.config new file mode 100644 index 0000000..9c05822 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/Enumerables-en/EnumFilesV1.cs b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV1.cs new file mode 100644 index 0000000..c257a2c --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV1.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Enumerable enumerates the text lines of a set of files + /// + public class EnumFilesV1 : IEnumerable + { + + private List _Lines; + + /// + /// Create a new enumerabl + /// + public EnumFilesV1(IEnumerable files) + { + // Init the files + this.Files = files.ToArray(); + + // Mark the lines list as 'to load' by setting it to null + _Lines = null; + } + + void LoadFiles() + { + // Create the lines list + _Lines = new List(); + + if (this.Files != null) + { + // For each file + foreach (var file in Files) + { + try + { + // Open a file text reader + using (var reader = new StreamReader(file)) + { + // Read each line of the file + String line; + while ((line = reader.ReadLine()) != null) + { + // Add the line to the list + _Lines.Add(line); + } + } + } + catch { } // When an error raised while reading the file, go to the next + } + } + } + + /// + /// Returns the lines enumertor + /// + public IEnumerator GetEnumerator() + { + // If the lines list is null then read the files + if (_Lines == null) + { + // Load the files + LoadFiles(); + } + + // Returns the list enumerator + return _Lines.GetEnumerator(); + } + + /// + /// Implements IEnumerator.GetEnumerator() (non generic version) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// List of the files + /// + public String[] Files { get; private set; } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-en/EnumFilesV2.cs b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV2.cs new file mode 100644 index 0000000..94bd31a --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV2.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Enumerable enumerates the text lines of a set of files, rebuilding the list on each call + /// + public class EnumFilesV2 : IEnumerable + { + + /// + /// Create a new enumerable + /// + public EnumFilesV2(IEnumerable files) + { + // Init the files + this.Files = files.ToArray(); + } + + IList LoadFiles() + { + // Create the lines list + var result = new List(); + + if (this.Files != null) + { + // For each file + foreach (var file in Files) + { + try + { + // Open a file text reader + using (var reader = new StreamReader(file)) + { + // Read each line of the file + String line; + while ((line = reader.ReadLine()) != null) + { + // Add the line of the list + result.Add(line); + } + } + } + catch { } // When an error raised while reading the file, go to the next + } + } + + // Returns the list + return result; + } + + /// + /// Returns the enumerator of the lines + /// + public IEnumerator GetEnumerator() + { + // Build the list + var list = LoadFiles(); + + // Return the list enumerator + return list.GetEnumerator(); + } + + /// + /// Implements IEnumerator.GetEnumerator() (non generic version) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// List of the files + /// + public String[] Files { get; private set; } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-en/EnumFilesV3.cs b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV3.cs new file mode 100644 index 0000000..cc27334 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV3.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Enumerable enumerates the text lines of a set of files via an enumerator + /// + public class EnumFilesV3 : IEnumerable + { + + /// + /// Create a new enumerable + /// + public EnumFilesV3(IEnumerable files) + { + // Init the files + this.Files = files.ToArray(); + } + + /// + /// Returns the lines enumerator + /// + public IEnumerator GetEnumerator() + { + // Returns a new enumerator + return new FileEnumerator(Files); + } + + /// + /// Implements IEnumerator.GetEnumerator() (non generic version) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// List of the files + /// + public String[] Files { get; private set; } + + } + + /// + /// File enumerator + /// + class FileEnumerator : IEnumerator + { + Func _CurrentState = null; + int _CurrentFilePos; + String _CurrentFileName; + TextReader _CurrentFile; + + /// + /// Create a new enumerator + /// + public FileEnumerator(String[] files) + { + // Init the files + this.Files = files; + // Init the enumerator + Current = null; + _CurrentFilePos = 0; + _CurrentFileName = null; + _CurrentFile = null; + // The enumerator state is to open the next file to read + _CurrentState = OpenNextFileState; + } + + /// + /// Dispose some resources + /// + public void Dispose() + { + // If we have a file opened we close it + if (_CurrentFile != null) + { + _CurrentFile.Dispose(); + _CurrentFile = null; + } + // Set the state to 'Completed' + _CurrentState = CompletedState; + } + + /// + /// Try to open the next file in the list + /// + bool GetNextFile() + { + String filename = null; + TextReader file = null; + while (file == null && Files != null && _CurrentFilePos < Files.Length) + { + try + { + filename = Files[_CurrentFilePos++]; + file = new StreamReader(filename); + } + catch { } + } + // If we have a file opened + if (file != null) + { + _CurrentFileName = filename; + _CurrentFile = file; + return true; + } + // Else we don't found + return false; + } + + /// + /// Open the next file + /// + bool OpenNextFileState() + { + // If we don't have file, we stop all + if (!GetNextFile()) + { + Current = null; + // Go to the state 'Completed' + _CurrentState = CompletedState; + // We finished + return false; + } + + // Go to the state ReadNextLine + _CurrentState = ReadNextLineState; + + // Read the first line + return _CurrentState(); + } + + /// + /// Read line state + /// + bool ReadNextLineState() + { + try + { + // Read the next line in the file + String line = _CurrentFile.ReadLine(); + // If the line is not null we process it + if (line != null) + { + Current = line; + return true; + } + // The line is null so we reach the end of the file, we release the resource + } + catch + { + // If an error raised while reading we close the current file to avoid infinite loops + + } + // Release resources to go to the next file + _CurrentFile.Dispose(); + _CurrentFile = null; + _CurrentFileName = null; + // Go to the state 'OpenNextFile' + _CurrentState = OpenNextFileState; + // Process the next state + return _CurrentState(); + } + + /// + /// The iteration is finished, so we returns always false + /// + bool CompletedState() + { + return false; + } + + /// + /// We don"t used this method + /// + public void Reset() + { + throw new NotSupportedException(); + } + + /// + /// We move to the next line + /// + public bool MoveNext() + { + // Process the next state + return _CurrentState(); + } + + /// + /// List odf the files + /// + public String[] Files { get; private set; } + + /// + /// Current value + /// + public string Current { get; private set; } + object System.Collections.IEnumerator.Current { get { return Current; } } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-en/EnumerableWithEnumeratorDisposable.cs b/Enumerables-Technet/Source/Enumerables-en/EnumerableWithEnumeratorDisposable.cs new file mode 100644 index 0000000..b6f25d0 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/EnumerableWithEnumeratorDisposable.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Enumerable class using disposable enumerators + /// + class EnumerableWithEnumeratorDisposable : IEnumerable + { + List _Source; + + /// + /// Create a new enumerable + /// + /// Data source + /// Action invoked when an enumerator was disposed + public EnumerableWithEnumeratorDisposable(IEnumerable source, Action onDispose = null) + { + _Source = new List(source); + OnDispose = onDispose; + } + + /// + /// Return a disposable enumerator + /// + public IEnumerator GetEnumerator() + { + return new EnumeratorDisposable(_Source, OnDispose); + } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Action invoked when an enumerator was disposed + /// + public Action OnDispose { get; set; } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-en/Enumerables-en.csproj b/Enumerables-Technet/Source/Enumerables-en/Enumerables-en.csproj new file mode 100644 index 0000000..eea64fc --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/Enumerables-en.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {6373827A-FF33-4A4F-A7F7-934903CD17EA} + Exe + Properties + Enumerables + Enumerables + v4.5.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + + + \ No newline at end of file diff --git a/Enumerables-Technet/Source/Enumerables-en/EnumeratorDisposable.cs b/Enumerables-Technet/Source/Enumerables-en/EnumeratorDisposable.cs new file mode 100644 index 0000000..917fcfe --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/EnumeratorDisposable.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Disposable enumerator + /// + class EnumeratorDisposable : IEnumerator, IDisposable + { + List _Source; + int _Current = -1; + + /// + /// Create a new enumerator + /// + /// Data source to enumerate + /// Action invoked when disposed + public EnumeratorDisposable(IEnumerable source, Action onDispose) + { + this._Source = new List(source); + this.OnDispose = onDispose; + } + /// + /// Dispose + /// + public void Dispose() + { + if (OnDispose != null) + OnDispose(); + } + /// + /// Current item + /// + public T Current { get; private set; } + object System.Collections.IEnumerator.Current { get { return Current; } } + /// + /// Move to the next item + /// + public bool MoveNext() + { + if (++_Current >= _Source.Count) return false; + Current = _Source[_Current]; + return true; + } + + public void Reset() + { + throw new NotImplementedException(); + } + + /// + /// Method invoked when the enumerator was disposed + /// + public Action OnDispose { get; set; } + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-en/FakeEnumerable.cs b/Enumerables-Technet/Source/Enumerables-en/FakeEnumerable.cs new file mode 100644 index 0000000..ced2d06 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/FakeEnumerable.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// Fake enumerable that don't implements IEnumerable but supported by 'foreach' + /// + class FakeEnumerable + { + /// + /// foreach require the class have only a public method GetEnumerator() returning an object with a public method + /// MoveNext() and e public property Current. + /// + public FakeEnumerator GetEnumerator() + { + return new FakeEnumerator(); + } + } + +} diff --git a/Enumerables-Technet/Source/Enumerables-en/FakeEnumerator.cs b/Enumerables-Technet/Source/Enumerables-en/FakeEnumerator.cs new file mode 100644 index 0000000..7a27fe6 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/FakeEnumerator.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Enumerables +{ + /// + /// Fake enumerator that don't implements IEnumerator but supported by 'foreach' + /// + /// + /// Need to implements a public method MoveNext() and a public property Current + /// + class FakeEnumerator + { + List _Source = new List(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + int _Current = -1; + /// + /// Current item + /// + public int Current { get; private set; } + /// + /// Move to the next item + /// + public bool MoveNext() + { + if (++_Current >= _Source.Count) return false; + Current = _Source[_Current]; + return true; + } + } +} diff --git a/Enumerables-Technet/Source/Enumerables-en/Files/Fichier1.txt b/Enumerables-Technet/Source/Enumerables-en/Files/Fichier1.txt new file mode 100644 index 0000000..0f2a04e --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/Files/Fichier1.txt @@ -0,0 +1,3 @@ +First line of the first file. +Second line of the first file. +Third line of the first file. diff --git a/Enumerables-Technet/Source/Enumerables-en/Files/Fichier2.txt b/Enumerables-Technet/Source/Enumerables-en/Files/Fichier2.txt new file mode 100644 index 0000000..be86a36 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/Files/Fichier2.txt @@ -0,0 +1,3 @@ +First line of the second file. +Second line of the second file. +Third line of the second file. diff --git a/Enumerables-Technet/Source/Enumerables-en/Files/Fichier3.txt b/Enumerables-Technet/Source/Enumerables-en/Files/Fichier3.txt new file mode 100644 index 0000000..3597ed8 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/Files/Fichier3.txt @@ -0,0 +1,3 @@ +First line of the third file. +Second line of the third file. +Third line of the third file. diff --git a/Enumerables-Technet/Source/Enumerables-en/Person.cs b/Enumerables-Technet/Source/Enumerables-en/Person.cs new file mode 100644 index 0000000..a5446b1 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/Person.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + class Person + { + public String Name { get; set; } + public DateTime? BirthDay { get; set; } + public bool IsFamily { get; set; } + } +} diff --git a/Enumerables-Technet/Source/Enumerables-en/Program.cs b/Enumerables-Technet/Source/Enumerables-en/Program.cs new file mode 100644 index 0000000..add1690 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/Program.cs @@ -0,0 +1,586 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + class Program + { + + /// + /// Returns a list of elements as enumerable + /// + /// Indicates is we returns an enumerable with disposable enumerator + static IEnumerable GetItems(bool asDisposable = false) + { + var items = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + return asDisposable ? new EnumerableWithEnumeratorDisposable(items, () => Console.WriteLine("Enumerator disposed")).AsEnumerable() : items; + } + + /// + /// List of the soure files + /// + static IEnumerable GetTestFileNames() + { + var dir = AppDomain.CurrentDomain.BaseDirectory; + return new String[]{ + Path.Combine(dir, "Files", "Fichier1.txt"), + Path.Combine(dir, "Files", "Fichier2.txt"), + Path.Combine(dir, "Files", "Fichier3.txt") + }; + } + + /// + /// List of persons + /// + static IEnumerable GetPersons() + { + yield return new Person() { + Name = "Toto", + BirthDay = null, + IsFamily = true + }; + yield return new Person() { + Name = "Titi", + BirthDay = new DateTime(2010, 11, 27), + IsFamily = false + }; + yield return new Person() { + Name = "Tata", + BirthDay = null, + IsFamily = false + }; + yield return new Person() { + Name = "Tutu", + BirthDay = new DateTime(1994, 8, 11), + IsFamily = true + }; + } + + #region Loop samples + + /// + /// foreach sample + /// + static void ForEach() + { + // Get the enumerable + IEnumerable enumerable = GetItems(); + + // Iterates each element in 'enumerable' + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + } + + /// + /// foreach sample with disposable enumerator + /// + static void ForEachDisposable() + { + // Get the enumerable + IEnumerable enumerable = GetItems(true); + + // Iterates each element in 'enumerable' + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + } + + /// + /// Basic iteration sample + /// + static void IterationBase() + { + // Get the enumerable + IEnumerable enumerable = GetItems(); + + // Get a new enumerator + IEnumerator enumerator = enumerable.GetEnumerator(); + // While the enumerator move + while (enumerator.MoveNext()) + { + Int32 elm = enumerator.Current; + // .. + Console.WriteLine(elm); + } + } + + /// + /// Iteration sample with IDiposable management like 'foreach' do + /// + static void IterationBaseWithDispose() + { + // Get the enumerable + IEnumerable enumerable = GetItems(true); + + // Get a new enumerator + IEnumerator enumerator = enumerable.GetEnumerator(); + try + { + // While the enumerator move + while (enumerator.MoveNext()) + { + Int32 elm = enumerator.Current; + // .. + Console.WriteLine(elm); + } + } + finally + { + // Check if the enumerator is disposable + IDisposable disp = enumerator as IDisposable; + // If true the we dispose it + if (disp != null) + { + disp.Dispose(); + } + } + } + + /// + /// foreach sample with an object not implementing IEnumerable/IEnumerator + /// + static void ForEachWithoutIEnumerable() + { + // Get the enumerable + var enumerable = new FakeEnumerable(); + + // Iterates each element in 'enumerable' + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + } + + /// + /// Test ReverseEnumerator + /// + static void TestReverse() + { + // ReverseEnumerator require an IList<> + IList list = new List(GetItems()); + + // Create the reverse enumerable + IEnumerable enumerable = new ReverseEnumerable(list); + + // Iterates each element in 'enumerable' + foreach (int elm in enumerable) + { + // .. + Console.WriteLine(elm); + } + + // Iterates each element and stop the loop when find the '5' element + foreach (int elm in enumerable) + { + if (elm == 5) break; + // .. + Console.WriteLine(elm); + } + + } + + #endregion + + #region Complex enumerators samples + + /// + /// Test of files enumerator first version + /// + static void TestFilesV1() + { + // Create the enumerable with tests files + var enumerable = new EnumFilesV1(GetTestFileNames()); + + // Iterates the enumerable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + + } + + /// + /// Test the files enumerator better than the first version + /// + static void TestFilesV2() + { + // Create the enumerable with tests files + var enumerable = new EnumFilesV2(GetTestFileNames()); + + // Iterates the enumerable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + + } + + /// + /// Test the files enumerator more better + /// + static void TestFilesV3() + { + // Create the enumerable with tests files + var enumerable = new EnumFilesV3(GetTestFileNames()); + + // Iterates the enumerable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + + // Iterates the enumerable and force a break + int i = 0; + foreach (var line in enumerable) + { + if (i++ >= 4) break; + Console.WriteLine(line); + } + } + + #endregion + + #region Yield method sample + + /// + /// Open a new file or null if an error raised + /// + static StreamReader OpenFile(String file) + { + try + { + return new StreamReader(file); + } + catch + { + return null; + } + } + /// + /// Enumerates the file with a Yield method + /// + static IEnumerable EnumFilesYield(IEnumerable files) + { + if (files != null) + { + // For each files + foreach (var file in files) + { + // Open a file text reader + using (var reader = OpenFile(file)) + { + // reader can be null if an error raised + if (reader != null) + { + // Read each line of the file + String line; + do + { + // Read the line, if an error raised we stop the loop + try + { + line = reader.ReadLine(); + } + catch + { + break; + } + // Returns the line in the 'enumerable' + if (line != null) + yield return line; + } while (line != null);// Loop while we have a line + } + } + } + } + } + /// + /// Test the Yield iteration + /// + static void TestFilesYield() + { + // Get an enumerable with the files test + var enumerable = EnumFilesYield(GetTestFileNames()); + + // Iterates the enumerable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + + // Iterates the enumerable and force a break + int i = 0; + foreach (var line in enumerable) + { + if (i++ >= 4) break; + Console.WriteLine(line); + } + + } + /// + /// Same as but without the exceptions management + /// + static IEnumerable EnumFilesYieldUnprotected(IEnumerable files) + { + if (files != null) + { + // For each file + foreach (var file in files) + { + // Open a file text reader + using (var reader = new StreamReader(file)) + { + // Read each line of the file + String line; + while ((line = reader.ReadLine()) != null) + { + // Returns the line in the enumerable + yield return line; + } + } + } + } + } + + /// + /// Complex Yield method + /// + static IEnumerable YieldComplex() + { + Random rnd = new Random(); + + // Direct emit + yield return "Start"; + + // Emit in a switch + for (int i = 0; i < 10; i++) + { + switch (rnd.Next(4)) + { + case 1: + yield return "Funky 1"; + break; + case 2: + continue; + case 3: + yield return "<<<<"; + // Emit an another enumerable + foreach (var line in EnumFilesYield(GetTestFileNames())) + { + // Set a condition + if (line.Contains("x")) + yield return line; + } + yield return ">>>>"; + break; + case 0: + default: + yield return "Funky 0"; + break; + } + } + + // Direct emit + yield return "End"; + } + + /// + /// Tesst the complex Yield method + /// + static void TestYieldComplex() + { + foreach (var item in YieldComplex()) + Console.WriteLine(item); + } + + #endregion + + #region LINQ samples + + /// + /// Simple LINQ request + /// + static void TestLinqRequest() + { + var query = from p in GetPersons() + where p.IsFamily + orderby p.Name + select p; + + foreach (var p in query) + Console.WriteLine(p.Name); + + foreach (var p in query.Take(2)) + Console.WriteLine(p.Name); + } + + /// + /// Simple LINQ request as fluent mode + /// + static void TestLinqRequestAsFluent() + { + var query = GetPersons() + .Where(p => p.IsFamily) + .OrderBy(p => p.Name) + ; + + foreach (var p in query) + Console.WriteLine(p.Name); + + foreach (var p in query.Take(2)) + Console.WriteLine(p.Name); + } + + //static void TestStats() + //{ + // var q = from stats in GetStats() + // where stat.Date >= lastRefresh + // orderby stat.Date + // select stats; + + // foreach (var stats in q) + // { + // // Display the stats + // } + + // lblLastUpdate.Text = String.Format("{0} : {1} new statistics", DateTime.Now, q.Count()); + // lastRefresh = DateTime.Now; + + //} + + /// + /// Where operator simulation + /// + static IEnumerable SimulWhere() + { + foreach (var p in GetPersons()) + { + if (p.IsFamily) + yield return p; + } + } + + /// + /// OrderBy operator simulation + /// + static IEnumerable SimulOrderBy() + { + List result = new List(); + result.AddRange(SimulWhere()); + result.Sort((x, y) => String.Compare(x.Name, y.Name)); + foreach (var p in result) + yield return p; + } + + /// + /// LINQ simulation + /// + static void TestSimulLinq() + { + var query = SimulOrderBy(); + + foreach (var p in query) + Console.WriteLine(p.Name); + + foreach (var p in query.Take(2)) + Console.WriteLine(p.Name); + } + + #endregion + + static void WaitAndPress() + { + Console.WriteLine("Press a key to continue..."); + Console.ReadKey(); + Console.WriteLine(); + } + static void Main(string[] args) + { + #region Loop samples + Console.WriteLine("* ForEach loop"); + ForEach(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* ForEach loop whith an IDisposable"); + ForEachDisposable(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Basic iteration (ForEach simulation)"); + IterationBase(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Basic iteration with a disposable (ForEach simulation)"); + IterationBaseWithDispose(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* ForEach loop with a non IEnumerable object"); + ForEachWithoutIEnumerable(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Test ReverseEnumerator"); + TestReverse(); + Console.WriteLine(); + WaitAndPress(); + #endregion + + #region Complex enumerators and yield method samples + Console.WriteLine("* Files enumerator V1 (basic way)"); + TestFilesV1(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Files enumerator V2 (better version than the first version)"); + TestFilesV2(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Files enumerator V3 (stream way version)"); + TestFilesV3(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Files enumerator with a Yield method"); + TestFilesYield(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Complex Yield method"); + TestYieldComplex(); + Console.WriteLine(); + WaitAndPress(); + #endregion + + #region LINQ samples + Console.WriteLine("* LINQ request"); + TestLinqRequest(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* Fluent LINQ request"); + TestLinqRequestAsFluent(); + Console.WriteLine(); + WaitAndPress(); + + Console.WriteLine("* LINQ simulation with methods"); + TestSimulLinq(); + Console.WriteLine(); + WaitAndPress(); + #endregion + } + } +} diff --git a/Enumerables-Technet/Source/Enumerables-en/Properties/AssemblyInfo.cs b/Enumerables-Technet/Source/Enumerables-en/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0e5e10e --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Les informations générales relatives à un assembly dépendent de +// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations +// associées à un assembly. +[assembly: AssemblyTitle("Enumerables-en")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Enumerables-en")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly +// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de +// COM, affectez la valeur true à l'attribut ComVisible sur ce type. +[assembly: ComVisible(false)] + +// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM +[assembly: Guid("a948ab06-3663-455f-be3a-7d83801eb949")] + +// Les informations de version pour un assembly se composent des quatre valeurs suivantes : +// +// Version principale +// Version secondaire +// Numéro de build +// Révision +// +// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut +// en utilisant '*', comme indiqué ci-dessous : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Enumerables-Technet/Source/Enumerables-en/ReverseEnumerable.cs b/Enumerables-Technet/Source/Enumerables-en/ReverseEnumerable.cs new file mode 100644 index 0000000..97e2242 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/ReverseEnumerable.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + /// + /// IEnumerable implementation to read a list in the reverse way + /// + public class ReverseEnumerable : IEnumerable + { + + /// + /// New enumerable class + /// + public ReverseEnumerable(IList source) + { + this.List = source; + } + + /// + /// IEnumerable<T>.GetEnumerator() implementation + /// + /// Create a new ReverseEnumerator + public IEnumerator GetEnumerator() + { + return new ReverseEnumerator(List); + } + + /// + /// IEnumerable.GetEnumerator() (non generic version) + /// + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Source list + /// + public IList List { get; private set; } + + } +} diff --git a/Enumerables-Technet/Source/Enumerables-en/ReverseEnumerator.cs b/Enumerables-Technet/Source/Enumerables-en/ReverseEnumerator.cs new file mode 100644 index 0000000..d13e191 --- /dev/null +++ b/Enumerables-Technet/Source/Enumerables-en/ReverseEnumerator.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Enumerables +{ + + /// + /// Enumerator iterates the list in the reverse way + /// + public class ReverseEnumerator : IEnumerator + { + IList _Source; + int _Position; + bool _Completed; + + /// + /// Create a new enumerator + /// + public ReverseEnumerator(IList source) + { + this._Source = source; + // Set -1 to indicates the iteration is not started + this._Position = -1; + // The iteration is not finished + this._Completed = false; + // Set the Current value by default + this.Current = default(T); + } + + /// + /// Release the resources + /// + public void Dispose() + { + // Nothing to dispose, but mark the iterator as finished + this._Completed = true; + Console.WriteLine("Enumerator disposed"); + } + + /// + /// This method is called when we want to reset the enumerator + /// + public void Reset() + { + // Set -1 to indicates the iteration is not started + this._Position = -1; + // The iteration is not finished + this._Completed = false; + // Set the Current value by default + this.Current = default(T); + } + + /// + /// We go to the next element + /// + /// False when the iteration is finished + public bool MoveNext() + { + // If the source is null then we have nothing to browse, the iteration is finished + if (this._Source == null) return false; + + // If the iteration is finished, we stop here + if (this._Completed) return false; + + // If the is -1 we get the count of the elements to iterates for starting the iteration + if (this._Position == -1) + { + Console.WriteLine("Iteration started"); + this._Position = _Source.Count; + } + + // We move on the list + this._Position--; + + // If we reach the -1 position the iteration is finished + if (this._Position < 0) + { + this._Completed = true; + Console.WriteLine("Iteration finished"); + return false; + } + + // We set Current and continue + Current = this._Source[this._Position]; + + return true; + } + + /// + /// Current element + /// + public T Current { get; private set; } + + /// + /// Current element for the non generic version + /// + object System.Collections.IEnumerator.Current + { + get { return Current; } + } + + } + +} diff --git a/Enumerables-Technet/Source/Enumerables.sln b/Enumerables-Technet/Source/Enumerables.sln index 42d03ab..0e9d277 100644 --- a/Enumerables-Technet/Source/Enumerables.sln +++ b/Enumerables-Technet/Source/Enumerables.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enumerables-fr", "Enumerables-fr\Enumerables-fr.csproj", "{BD6CD85F-9196-4DFB-BEDC-560004EB982A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enumerables-en", "Enumerables-en\Enumerables-en.csproj", "{6373827A-FF33-4A4F-A7F7-934903CD17EA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {BD6CD85F-9196-4DFB-BEDC-560004EB982A}.Debug|Any CPU.Build.0 = Debug|Any CPU {BD6CD85F-9196-4DFB-BEDC-560004EB982A}.Release|Any CPU.ActiveCfg = Release|Any CPU {BD6CD85F-9196-4DFB-BEDC-560004EB982A}.Release|Any CPU.Build.0 = Release|Any CPU + {6373827A-FF33-4A4F-A7F7-934903CD17EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6373827A-FF33-4A4F-A7F7-934903CD17EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6373827A-FF33-4A4F-A7F7-934903CD17EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6373827A-FF33-4A4F-A7F7-934903CD17EA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From a59cee103fda86fd90298225dceae888971b14b1 Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Sun, 15 Nov 2015 17:55:33 +0100 Subject: [PATCH 08/11] =?UTF-8?q?Int=C3=A9gration=20de=20l'article=20en=20?= =?UTF-8?q?fran=C3=A7ais=20avec=20les=20trois=20parties=20du=20blog=20en?= =?UTF-8?q?=20un=20seul=20article.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Enumerables-Technet/Article/article-fr.md | 878 +++++++++++++++++++++ Enumerables-Technet/README.md | 1 + Enumerables-Technet/Source/Enumerables.sln | 7 +- 3 files changed, 885 insertions(+), 1 deletion(-) create mode 100644 Enumerables-Technet/Article/article-fr.md diff --git a/Enumerables-Technet/Article/article-fr.md b/Enumerables-Technet/Article/article-fr.md new file mode 100644 index 0000000..c488559 --- /dev/null +++ b/Enumerables-Technet/Article/article-fr.md @@ -0,0 +1,878 @@ +# Les Enumérables en détails + +Cet article traite en détail du fonctionnement des énumérables en C#. + +Cet article est basé sur une série d'articles disponibles [sur ce blog](http://www.yeg-projects.com/2015/01/soyons-enumerables-partie-1/) écrit par Yan Grenier. + +Tous les exemples de code se trouvent dans le projet "Enumerable-fr" de la solution. + +# Les énumérables + +Le principe des énumérables est de permettre de parcourir une liste d'éléments en récupérant élément par élément, a la manière d'un flux (on parle d'itération ou d'énumération). L'utilisation la plus courante de ce principe se fait avec le mot clé `foreach`. + +Dans .Net les énumérables fonctionnent via les interfaces `System.Collections.IEnumerable` et `System.Collections.IEnumerator`. Ces interfaces existent en version générique `System.Collections.Generic.IEnumerable<>` et `System.Collections.Generic.IEnumerator<>`. + +A partir du moment où une classe implémente l'interface `IEnumerable` alors elle devient énumérable. + +`IEnumerable` indique que l'on peut énumérer un objet et il a la charge de fournir un \ `IEnumerator` à chaque fois que l'on veut parcourir ses éléments. + +`IEnumerator` est "l'énumérateur", c'est à dire l'objet qui va se charger de "parcourir" les éléments à énumérer. + +Donc pour rendre une classe énumérable, on implémente `IEnumerable` qui va créer un `IEnumerator` qui aura la charge de l'itération des éléments. + +A savoir: toutes les listes, collections, dictionnaires, ainsi que les tableaux du .Net, implémentent `IEnumerable`. + +## Interface IEnumerable/IEnumerable<T> + +Cette interface indique qu'on peut énumérer l'objet de la classe qui l'implémente. Elle ne possède qu'une méthode `GetEnumerator()` retournant un `IEnumerator`. + +```csharp +public interface IEnumerable +{ + IEnumerator GetEnumerator(); +} +public interface IEnumerable : IEnumerable +{ + IEnumerator GetEnumerator(); +} +``` + +Le principe de cette interface et de fournir un nouvel objet énumérateur à chaque appel permettant ainsi de démarrer une nouvelle itération, et permettre également d'avoir plusieurs itérations en parallèle sur la même source (si la logique de l'objet sous-jacent le permet). + +## Interface IEnumerator/IEnumerator<T> + +Cette interface représente la logique d'itération (objet énumérateur). C'est elle qui indique l'élément en cours et qui permet de se "déplacer" dans l'énumération. + +```csharp +public interface IEnumerator +{ + object Current { get; } + bool MoveNext(); + void Reset(); +} +public interface IEnumerator : IDisposable, IEnumerator +{ + T Current { get; } +} +``` + +Le fonctionnement de l'énumérateur est assez simple : il indique l'élément en cours d'énumération (propriété `Current`), et se déplace vers le prochain élément avec la méthode `MoveNext()`. + +## Principe de l'itération + +Pour parcourir un énumérable on utilise toujours le même principe : + +- On demande à `IEnumerable` un nouvel énumérateur +- Tant que `IEnumerator.MoveNext()` retourne `true` + - On traite `IEnumerator.Current` + +C'est ce que fait l'instruction `foreach` pour nous. + +Ainsi la boucle suivante (méthode `ForEach()` dans le programme d'exemple) : + +```csharp +// Récupération de l'énumérable +IEnumerable enumerable = GetItems(); +// Parcours chaque élément dans 'enumerable' +foreach (int elm in enumerable) +{ + // .. +} +``` + +est approximativement compilée comme ceci (méthode `IterationBase()` dans le programme d'exemple) : + +```csharp +// Récupération de l'énumérable +IEnumerable enumerable = GetItems(); +// Récupère un nouvel énumérateur +IEnumerator enumerator = enumerable.GetEnumerator(); +// Tant que l'énumérateur se déplace +while (enumerator.MoveNext()) +{ + Int32 elm = enumerator.Current; + // .. +} +``` + +Toutefois ce n'est pas tout à fait exact, car la boucle `foreach` gère également la possibilité qu'un énumérateur soit disposable (implémentant `IDisposable`) (la méthode `ForEachDisposable()` le montre), donc la boucle équivalente est en réalité plus comme ceci (méthode `IterationBaseWithDispose()` dans le programme d'exemple) : + +```csharp +// Récupération de l'énumérable +IEnumerable enumerable = GetItems(true); +// Récupère un nouvel énumérateur +IEnumerator enumerator = enumerable.GetEnumerator(); +try +{ + // Tant que l'énumérateur se déplace + while (enumerator.MoveNext()) + { + Int32 elm = enumerator.Current; + // .. + Console.WriteLine(elm); + } +} +finally +{ + // On détermine si l'énumérateur est disposable + IDisposable disp = enumerator as IDisposable; + // Si c'est le cas on dispose l'énumérateur + if (disp != null) + { + disp.Dispose(); + } +} +``` + +De cette manière que la boucle s'arrête normalement, ou prématurément via un `break`, ou à cause d'une exception, notre énumérateur sera disposé. + +Il faut comprendre qu'implémenter `IDisposable` sur notre énumérateur est le seul moyen que nous avons pour déterminer qu'une itération est terminée, surtout si on a pas parcouru l'ensemble des éléments. + +Petit aparté : en réalité `foreach` ne prend pas en charge que des `IEnumerable`. Ce qui importe pour le compilateur lors d'un `foreach` c'est que la source à énumérer possède une méthode `GetEnumerator()` publique qui retourne un type qui possède une méthode `MoveNext()` retournant un booléen, et une propriété `Current`. Dans le programme d'exemple vous trouverez la méthode `ForEachWithoutIEnumerable()` utilisant les objets `FakeEnumerable` et `FakeEnumerator` n'implémentant pas les interfaces. + +## Les énumérateurs + +Pour faire un énumérateur il faut donc implémenter `IEnumerator`. Toutefois avant d'en implémenter un, si vous voulez énumérer un tableau privé (ou tout autre IEnumerable) par exemple, il vous suffit de renvoyer le `GetNumerator()` du tableau. + +L'implémentation est assez simple au final, ce qui importe c'est qu'à sa création l'énumérateur est dans un état "indéfini", c'est à dire qu'on ne s'est pas encore déplacé, donc `Current` doit se trouver avec une valeur par défaut, et on doit attendre le premier `MoveNext()` pour commencer vraiment notre itération. Ce qui implique que si vous avez des initialisations à faire (se connecter à une base de données par exemple) vous devez le faire lors du premier `MoveNext()` pour des raisons d'optimisation; on peut avoir besoin d'instancier un énumérateur sans pour autant le parcourir, ce qui peut être le cas quand on enchaîne plusieurs énumérables comme LINQ (cf prochaine partie de cet article). + +Imaginons que nous voulons créer un énumérateur qui parcours une liste à l'envers. La méthode `TestReverse()` du programme d'exemple montre l'utilisation de notre classe énumérateur. + +```csharp +/// +/// Enumérateur parcourant une liste dans le sens inverse +/// +public class ReverseEnumerator : IEnumerator +{ + IList _Source; + int _Position; + bool _Completed; + + /// + /// Création d'un nouvel énumérateur + /// + public ReverseEnumerator(IList source) + { + this._Source = source; + // On met -1 pour indiquer qu'on a pas commencé l'itération + this._Position = -1; + // L'itération n'est pas terminée + this._Completed = false; + // On défini Current avec la valeur par défaut + this.Current = default(T); + } + + /// + /// Libération des ressources + /// + public void Dispose() + { + // On a rien à libérer , mais on marque notre itérateur comme terminé + this._Completed = true; + } + + /// + /// Cette méthode est appelée lorsque l'on veut réinitialiser l'énumérateur + /// + public void Reset() + { + // On met -1 pour indiquer qu'on a pas commencer l'itération + this._Position = -1; + // L'itération n'est pas terminée + this._Completed = false; + // On défini Current avec la valeur par défaut + this.Current = default(T); + } + + /// + /// On se déplace vers le prochain élément + /// + /// False lorsque l'itération est terminée + public bool MoveNext() + { + // Si la source est Null alors on a rien à parcourir, donc l'itération s'arrête + if (this._Source == null) return false; + + // Si l'itération est terminée alors on ne va pas plus loin + if (this._Completed) return false; + + // Si la position est à -1 on récupère le nombre d'éléments à parcourir pour démarrer l'itération + if (this._Position == -1) + { + this._Position = _Source.Count; + } + + // On se déplace dans la liste + this._Position--; + + // Si on a atteind -1 alors on a terminé l'itération + if (this._Position < 0) + { + this._Completed = true; + return false; + } + + // On défini Current et on continue + Current = this._Source[this._Position]; + + return true; + } + + /// + /// Elément en cours de l'itération + /// + public T Current { get; private set; } + + /// + /// Elément en cours pour la version non générique + /// + object System.Collections.IEnumerator.Current + { + get { return Current; } + } + +} +``` + +Comme on peut le voir le principe de l'implémentation est assez simple. + +Toutefois ça peut vite se compliquer quand nous avons des énumérateurs complexes (parcours d'un arbre syntaxique par exemple). Nous verrons au chapitre suivant comment on peut se simplifier la vie. + +Dernier point pour cette partie : `IEnumerator` demande l'implémentation de `Reset()`. Il faut savoir que cette méthode n'est pas vraiment utilisée, la plupart du temps elle lève une exception `NotImplementedException`. Donc si son implémentation est trop complexe, n'ayez pas peur de faire de même. En fait on part du principe que si on veut redémarrer une itération on fait à nouveau appel à `IEnumerable.GetEnumerator()` qui va nous instancier un nouvel énumérateur, donc le reset n'a plus lieu d'être. + +# Les méthodes Yield + +Précédemment, certains d'entre vous ont dû se dire qu'à chaque fois que l'on veut mettre en place un énumérateur un peu particulier, il y a beaucoup de code à mettre place. + +Effectivement on peut vite se retrouver avec des énumérateurs complexes, avec interception d'erreurs, libération de ressources multiples, etc., ce qui peut rapidement compliquer les choses. + +C'est là que le compilateur C# va venir à notre rescousse via le mot clé yield (VB.NET supporte également une instruction `Yield`). + +Bien imaginons par exemple que nous voulons un énumérable auquel nous transmettons une liste de fichiers, et cet énumérable va parcourir chaque fichier et énumérer chaque ligne de texte du fichier. + +Cet exemple est intéressant car il inclus un gestion des erreurs (un fichier peut ne pas exister, ou être verrouillé, etc.) et une gestion des ressources multiples (chaque fichier doit être disposé). + +## Version brut de code + +Commençons par une version ressemblant typiquement à la solution que va prendre quelqu'un qui ne comprends pas les principes des énumérables ou tout simplement qui cherche à éviter la difficulté de faire un énumérateur correct. + +Cette solution se contente de lire tous les fichiers dans une liste, et lorsqu'on à besoin d'un énumérateur on retourne celui de la liste. (La méthode `TestFilesV1()` dans le programme exemple montre son utilisation). + +```csharp +/// +/// Enumérable parcourant les lignes texte d'un ensemble de fichier +/// +public class EnumFilesV1 : IEnumerable +{ + private List _Lines; + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV1(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + // On marque la liste des lignes à charger en la mettant à null + _Lines = null; + } + void LoadFiles() + { + // Création de la liste des lignes + _Lines = new List(); + if (this.Files != null) + { + // Pour chaque fichier + foreach (var file in Files) + { + try + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + _Lines.Add(line); + } + } + } + catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier + } + } + } + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // Si la liste des lignes est null alors il faut lire les fichiers + if (_Lines == null) + { + // Chargement des fichiers + LoadFiles(); + } + // Retourne l'énumérateur de la liste + return _Lines.GetEnumerator(); + } + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } +} +``` + +Alors qu'est-ce qui ne va pas avec ce code ? + +La première chose c'est que si les fichiers venaient à changer de contenu entre deux appels de `GetEnumerator()` nous retournerons à chaque fois le contenu de la lecture initiale. On peut résoudre ce problème simplement en modifiant légèrement notre code pour reconstruire la liste des lignes à chaque appel. + +```csharp +/// +/// Enumérable parcourant les lignes texte d'un ensemble de fichier reconstruisant une liste à chaque appel +/// +public class EnumFilesV2 : IEnumerable +{ + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV2(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + } + IList LoadFiles() + { + // Création de la liste des lignes + var result = new List(); + if (this.Files != null) + { + // Pour chaque fichier + foreach (var file in Files) + { + try + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + result.Add(line); + } + } + } + catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier + } + } + // On retourne la liste + return result; + } + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // On construit la liste + var list = LoadFiles(); + // Retourne l'énumérateur de la liste + return list.GetEnumerator(); + } + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } +} +``` + +On supprime la liste des lignes de l'énumérable et on construit une nouvelle liste à chaque appel de `GetEnumerator()` et on retourne l'énumerateur de cette nouvelle liste. (La méthode TestFilesV2() dans le programme exemple montre son utilisation). + +Cette fois nous respectons un peu mieux les principes des énumérables toutefois ce n'est toujours pas satisfaisant d'un point de vue des performances. En effet s'il s'avère que les fichiers sont volumineux, nous chargeons tout dans une liste en mémoire. Imaginons que nous nous servons de cet énumérable pour filtrer quelques lignes sur un million, nous pouvons engorger notre mémoire inutilement. Pire si on extrait que les milles premières lignes, nous aurons chargé 999000 lignes de trop :( + +## Version plus subtile + +L'idéal serait de ne lire une ligne de texte que quand on en a besoin. Bref en gros lors du `IEnumerator.MoveNext()`. + +Nous allons donc faire preuve de subtilité et gérer la lecture en flux. + +```csharp +/// +/// Enumérable parcourant les lignes texte d'un ensemble de fichier via un énumérateur +/// +public class EnumFilesV3 : IEnumerable +{ + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV3(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + } + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // Retourne un nouvel énumérateur + return new FileEnumerator(Files); + } + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } +} +/// +/// Enumérateur de fichier +/// +class FileEnumerator : IEnumerator +{ + Func _CurrentState = null; + int _CurrentFilePos; + String _CurrentFileName; + TextReader _CurrentFile; + /// + /// Création d'un nouvel énumérateur + /// + public FileEnumerator(String[] files) + { + // Initialisation des fichiers + this.Files = files; + // Initialisation de l'énumérateur + Current = null; + _CurrentFilePos = 0; + _CurrentFileName = null; + _CurrentFile = null; + // L'état de l'énumérateur est à l'ouverture du prochain fichier à traiter + _CurrentState = OpenNextFileState; + } + /// + /// Libération des ressources éventuelles + /// + public void Dispose() + { + // Si on a un fichier encore d'ouvert on libère la mémoire + if (_CurrentFile != null) + { + _CurrentFile.Dispose(); + _CurrentFile = null; + } + // On défini l'état 'Completed' + _CurrentState = CompletedState; + } + /// + /// Essayes d'ouvrir le prochain fichier de la liste + /// + bool GetNextFile() + { + String filename = null; + TextReader file = null; + while (file == null && Files != null && _CurrentFilePos < Files.Length) + { + try + { + filename = Files[_CurrentFilePos++]; + file = new StreamReader(filename); + } + catch { } + } + // Si on a un fichier d'ouvert + if (file != null) + { + _CurrentFileName = filename; + _CurrentFile = file; + return true; + } + // Sinon on a rien trouvé + return false; + } + /// + /// Ouverture du prochain fichier + /// + bool OpenNextFileState() + { + // Si on a pas ou plus de fichier on arrête tout + if (!GetNextFile()) + { + Current = null; + // On passe à l'état 'Completed' + _CurrentState = CompletedState; + // On termine + return false; + } + // On passe à l'état ReadNextLine + _CurrentState = ReadNextLineState; + // On lit la première ligne + return _CurrentState(); + } + /// + /// On est en cours de lecture + /// + bool ReadNextLineState() + { + try + { + // On lit la prochaine ligne du fichier + String line = _CurrentFile.ReadLine(); + // Si la ligne n'est pas null on la traite + if (line != null) + { + Current = line; + return true; + } + // La ligne est null alors on a atteint la fin du fichier, on libère sa ressource + } + catch + { + // Si une erreur survient à la lecture on ferme le fichier en cours pour éviter les boucles infinies + } + // Libération des ressources pour passer au fichier suivant + _CurrentFile.Dispose(); + _CurrentFile = null; + _CurrentFileName = null; + // On passe à l'état 'OpenNextFile' + _CurrentState = OpenNextFileState; + // On traite le prochain état + return _CurrentState(); + } + /// + /// L'itération est terminée on retourne toujours false + /// + bool CompletedState() + { + return false; + } + /// + /// On ne s'occupe pas de cette méthode + /// + public void Reset() + { + throw new NotSupportedException(); + } + /// + /// On se déplace + /// + public bool MoveNext() + { + // Exécution de l'état en cours + return _CurrentState(); + } + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + /// + /// Valeur en cours + /// + public string Current { get; private set; } + object System.Collections.IEnumerator.Current { get { return Current; } } +} +``` + +Donc cette fois la partie lecture est défini dans un énumérateur. Il s'agit d'une simple machine à états : 'OpenNextFile', 'ReadNextFile' et 'Completed'. + +On constate qu'il faut être vigilant à chaque endroit où une erreur peut survenir, ne pas oublier de libérer les ressources en fonction de différentes situations, etc. + +En revanche nous lisons nos fichiers ligne par ligne, par conséquent la charge mémoire est à son minimum. En cas de dispose on libère le fichier ouvert, et on se place sur l'état 'Completed' pour que l'énumérateur ne puisse plus rien faire. + +Pour gérer tout ce petit monde on a pas mal de ligne de code. + +Et pour tester tout ca (méthode `TestFilesV3()`) on utilise le code suivant : + +```csharp +private static void TestFilesV3() +{ + // Création de l'énumérable avec les fichiers de tests + var enumerable = new EnumFilesV3(GetTestFileNames()); + // On parcours l'énumérable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + // On parcours l'énumérable et provoque un arrêt prématuré + int i = 0; + foreach (var line in enumerable) + { + if (i++ >= 4) break; + Console.WriteLine(line); + } +} +``` + +Qui va parcourir une fois toutes les lignes de tous les fichiers, et une seconde fois mais uniquement les 4 premières lignes de la liste. + +## 'yield' à notre secours + +Donc nous avons deux situations : + +- soit nous faisons un code assez court facilement maintenable, mais qui risque de nous poser des problèmes techniques. +- soit nous faisons un code plus performant, mais qui est plus complexe, long et difficile à maintenir. + + +Et si on pouvait concilier les deux ? + +Par exemple : + +```csharp +/// +/// Ouvre un nouveau fichier ou retourne null si une erreur à lieu +/// +static StreamReader OpenFile(String file) +{ + try + { + return new StreamReader(file); + } + catch + { + return null; + } +} +static IEnumerable EnumFilesYield(IEnumerable files) +{ + if (files != null) + { + // Pour chaque fichier + foreach (var file in files) + { + // Ouverture d'un lecteur de fichier texte + using (var reader = OpenFile(file)) + { + // reader peut être null si une erreur à eu lieu + if (reader != null) + { + // Lecture de chaque ligne du fichier + String line; + do + { + // Lecture d'une ligne, si une erreur à lieu on arrête la boucle + try + { + line = reader.ReadLine(); + } + catch + { + break; + } + // On envoi la ligne d'énumérable + if (line != null) + yield return line; + } while (line != null);// Boucle tant qu'on a une ligne + } + } + } + } +} +private static void TestFilesYield() +{ + // Récupération d'un énumérable avec les fichiers de tests + var enumerable = EnumFilesYield(GetTestFileNames()); + // On parcours l'énumérable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + // On parcours l'énumérable et provoque un arrêt prématuré + int i = 0; + foreach (var line in enumerable) + { + if (i++ >= 4) break; + Console.WriteLine(line); + } +} +``` + +`TestFilesYield()` est une méthode qui lance deux boucles d'un énumérable renvoyé par la méthode `EnumFilesYield()`. + +C'est cette dernière qui nous intéresse, alors petite explication de code. On constate que cette méthode retourne un `IEnumerable`, en revanche elle ne renvoie jamais d'énumérable, a la place on a dans la double boucle de lecture de fichier une instruction `yield return line;` ou `line` est une `string`. + +Le `yield return` dans une méthode indique au compilateur que cette méthode est en fait un énumérateur et que chaque `yield return` renvoi un élément de cet énumérateur. Techniquement le compilateur va transformer cette méthode en un objet `IEnumerator` qui va simuler le code de la méthode. + +En plus du `yield return` il existe le `yield break` qui arrête l'énumérateur. + +Globalement le compilateur est capable de convertir la plupart du code en énumérateur, toutefois on ne peut pas utiliser `yield return` dans un try-catch (mais on le peut dans un try-finally). En revanche un `yield break` peut se trouver dans un try-catch mais pas un try-finally. + +C'est pour ça que notre code est un peu plus compliqué qu'une simple double boucle pour prendre en compte d'éventuelles erreurs. Mais malgré celà il reste toujours plus court et facile à maintenir que notre énumérateur précédent. + +L'énumérateur généré supporte le `IDisposable` par exemple dans notre cas si l'itération se termine en cours de lecture d'un fichier, comme il y a un `using`, le compilateur se chargera de créer le code nécessaire pour disposer la ressource se trouvant dans le `using`. De même que si dans une boucle `foreach` une exception est levée, et que l'on a un bloc `finally`qui englobe le `yield return` en cours alors ce bloc `finally` sera exécuté. + +Le mot clé `yield` peut être utilisé dans une méthode qui retourne `IEnumerable` mais également `IEnumerator` ce qui nous permet d'implémenter `IEnumerable.GetEnumerator()` par une méthode `yield`. + +Dernier point, l'énumérateur généré par le compilateur ne prend pas en charge `IEnumerator.Reset()` une exception `NotSupportedException` est levée. + +Pour plus d'informations sur `yield` et les itérateurs, voici quelques liens : + +- [Référence C# de yield](https://msdn.microsoft.com/fr-fr/library/9k7k7cf0.aspx) +- [Les itérateurs en C# et VB.net](https://msdn.microsoft.com/fr-fr/library/dscyy5s0.aspx) + + +## Pour en finir avec 'yield' + +L'utilisation de `yield` est très pratique, elle permet de gérer des scénarios complexes avec un code 'classique'. La seule vraie difficulté réside dans la gestion des exceptions, qui peut compliquer notre code, mais de manière générale notre code reste toujours plus simple. + +Le compilateur et le debuggeur de Visual Studio sont extrêment performants et vous permettent de faire du pas à pas dans l'énumérateur généré par `yield`, et ainsi de tracer exactement ce qu'il se passe dans l'énumérateur, même si le code dans la méthode yield est complexe, la trace suit parfaitement votre code. + +Le programme d'exemple contient les différents exemples donnés, plus quelques méthodes `yield` suplémentaires pour montrer qu'on peut faire des choses complexes. + +# LINQ et les énumérables à la chaîne + +LINQ est un language de requêtage intégré au language C# ou VB.Net, le propos de cette partie n'est pas d'expliquer LINQ en lui-même mais son comportement avec les énumérables dans la suite des deux précédentes parties. + +Pour plus d'informations sur LINQ je vous renvoie [à la documentation officielle](https://msdn.microsoft.com/fr-fr/library/bb397926.aspx). + +## Rappel + +Une requête LINQ ressemble à celà (méthode `TestLinqRequest()`) : + +```csharp +var query = from p in GetPersons() + where p.IsFamily + orderby p.Name + select p; +``` + +Cette requête n'est pas très compliquée : `GetPersons()` est une méthode qui retourne un `IEnumerable`, et notre requête en filtre les éléments dont le booléen `IsFamily` est à `true` et retourne le résultat trié par leur nom. + +Si on regarde de plus près la variable `query` (dans VisualStudio il suffit de survoler le mot clé **var** pour afficher une info-bulle informant du type déterminé par le compilateur) on constate qu'il s'agit d'un `IEnumerable`. + +En réalité il faut savoir que le compilateur va compiler cette requête sous la forme suivante (méthode `TestLinqRequestAsFluent()`) : + +```csharp +var query = GetPersons() + .Where(p => p.IsFamily) + .OrderBy(p => p.Name) + ; +``` + +LINQ fourni un ensemble de méthodes d'extensions (Select, Where, OrderBy, etc.) que l'on nomment "opérateur" car ils correspondent aux mot clés du language LINQ. + +Si on regarde de plus près les opérateurs de notre requête on constate qu'ils renvoient tous un `IEnumerable<>`. En fait chaque appel de ces opérateurs va créer un objet "proxy" de type énumérable qui encapsule l'énumérable "source". + +Ainsi notre variable `query` est en réalité une "chaîne" d'énumérables, elle contient un énumérable de type "OrderBy" qui se base sur un énumérable de type "Where" qui se base sur l'énumérable renvoyé par `GetPersons()`. + +## Mais alors, quel est le problème ? + +Voici une petite histoire vécue: un matin réception du mail suivant : + +> Je possède une table de données extrêmement volatile où je peux avoir une centaine de lignes insérées par seconde. +> +> Mon application affiche un tableau de bord avec des stats réactualisées régulièrement. Hors depuis que je suis en production LINQ calcul un nombre de lignes actualisées différents du nombre de lignes traitées, je ne comprends pas ! +> +> Voici mon code +> +> ```csharp +> class Stats { +> private DateTime lastRefresh; +> ... +> public void Refresh(){ +> var q = from stats in GetStats() +> where stat.Date >= lastRefresh +> orderby stat.Date +> select stats; +> +> foreach (var stats in q) +> { +> // Affichage de la stat +> } +> +> lblLastUpdate.Text = String.Format("{0} : {1} nouvelles statistiques", DateTime.Now, q.Count()); +> lastRefresh = DateTime.Now; +> } +> } +> ``` + +La source de données est un contexte Entity Framework, qui gère en réalité des `IQueryable` mais ces derniers sont des `IEnumerable`. + +Après discussion il s'avère que cette persone n'avait pas compris que sa requête `q` était un énumérable et que par conséquent chaque fois qu'il faisait une itération, un appel à GetEnumerator() était effectué et dans son cas une nouvelle requête SQL était exécutée. Hors l'opérateur `Count()` qu'il utilise pour déterminer le nombre de nouvelles statistiques ne renvoie pas un `IEnumerable` mais provoque une itération pour compter le nombre d'éléments. Seulement ses données étant tellement volatiles qu'entre les deux requêtes de nouvelles statistiques sont apparues. + +Le problème est là : en fait certaines personnes pensent qu'une requête LINQ est une sorte de liste tampon, qu'une fois qu'on a lancé une requête le résultat reste en mémoire, donc faire un `Count()` derrière se contente de compter le résultat. + +C'était la logique de cette personne, habituée à utiliser des principes de **Recordset** ou de **Dataset** pour les données, elle pensait qu'une requête LINQ était une sorte d'objet auto-alimenté contenant le résultat de sa requête. + +## Penser 'flux' + +Comme nous l'avons vu en début d'article, les énumérateurs sont une logique de "flux" ou on traite élément par élément. Dans le cadre d'une base de données chaque ligne étant retournée dés qu'elle est reçue du serveur. + +Nous avons vu également qu'un énumérable n'est qu'un fournisseur d'énumérateur, tant que l'on ne provoque pas l'énumération (appel à GetEnumerator()) nous n'avons pas de flux. + +Il a été également expliqué que LINQ était en fait une chaîne d'énumérable. Notre requête en début de cette partie pourrait ressembler à cela : + +```csharp +tatic IEnumerable SimulWhere() +{ + foreach (var p in GetPersons()) + { + if(p.IsFamily) + yield return p; + } +} +static IEnumerable SimulOrderBy() +{ + List result = new List(); + result.AddRange(SimulWhere()); + result.Sort((x, y) => String.Compare(x.Name, y.Name)); + foreach (var p in result) + yield return p; +} +static void TestSimulLinq() +{ + var query = SimulOrderBy(); + foreach (var p in query) + Console.WriteLine(p.Name); + foreach (var p in query.Take(2)) + Console.WriteLine(p.Name); +} +``` + +Dans la méthode `TestSimulLinq()` vous pouvez faire un pas à pas pour voir que tant qu'on n'entre pas dans un `foreach`, `SimulOrderBy()` n'est pas appelée. En revanche dans notre exemple elle est appelée deux fois car on a deux `foreach`. + +Donc il faut toujours penser nos requêtes en flux, qui se déclenchent à chaque fois que l'on provoque une itération. + +Alors une question se pose: comment savoir qu'une itération est déclenchée avec les opérateurs LINQ ? En bien c'est simple, il suffit de regarder le type de retour de l'opérateur, s'il renvoi un `IEnumerable` il ne provoque pas d'itération, mais renvoi un nouvel objet énumérable avec son propre énumérateur pour appliquer son opération sur le flux. + +Le langage LINQ intégré ne génère que des énumérables, par exemple `Count()` n'a pas d'équivalent LINQ, il faut utiliser la méthode, mais on peut coupler les deux : + +```csharp +int count = (from p in GetPersons() + where p.IsFamily + orderby p.Name + select p).Count(); +``` + +# Conclusion + +En espérant que cet article vous a apporté plus de clarté sur la logique des énumérables : + +- que `IEnumerable` ne contient pas de données, mais sert uniquement a créer un nouveau flux d'élément à chaque `GetEnumerator()` +- qu'une requête LINQ n'est pas un ensemble de données mais un énumérable +- qu'à chaque fois que vous avez un énumérable, il faut penser flux, quelque soit l'implémentation de cet énumérable + +# Voir Aussi + +- [Article d'origine](http://www.yeg-projects.com/2015/01/soyons-enumerables-partie-1/) +- [MSDN: Référence C# de yield](https://msdn.microsoft.com/fr-fr/library/9k7k7cf0.aspx) +- [MSDN: Les itérateurs en C# et VB.net](https://msdn.microsoft.com/fr-fr/library/dscyy5s0.aspx) +- [MSDN: Documentation officielle de LINQ](https://msdn.microsoft.com/fr-fr/library/bb397926.aspx) diff --git a/Enumerables-Technet/README.md b/Enumerables-Technet/README.md index 3af301b..592396a 100644 --- a/Enumerables-Technet/README.md +++ b/Enumerables-Technet/README.md @@ -3,3 +3,4 @@ Enumérables - Version Technet Ce projet est une refonte des articles sur les énumérables pour une version Wiki Technet. +This project is a rewrite of my articles about enumerables for a Wiki Technet version. diff --git a/Enumerables-Technet/Source/Enumerables.sln b/Enumerables-Technet/Source/Enumerables.sln index 0e9d277..df840d1 100644 --- a/Enumerables-Technet/Source/Enumerables.sln +++ b/Enumerables-Technet/Source/Enumerables.sln @@ -1,12 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +VisualStudioVersion = 12.0.40629.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enumerables-fr", "Enumerables-fr\Enumerables-fr.csproj", "{BD6CD85F-9196-4DFB-BEDC-560004EB982A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enumerables-en", "Enumerables-en\Enumerables-en.csproj", "{6373827A-FF33-4A4F-A7F7-934903CD17EA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Articles", "Articles", "{83ADC8F3-65D4-4FF8-8B52-9614700F9E86}" + ProjectSection(SolutionItems) = preProject + ..\Article\article-fr.md = ..\Article\article-fr.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 9191b288057d180dae4f81c24d1c939aa7ea80ab Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Sun, 15 Nov 2015 18:03:56 +0100 Subject: [PATCH 09/11] Prepare the english version. --- Enumerables-Technet/Article/article-en.md | 883 +++++++++++++++++++++ Enumerables-Technet/Source/Enumerables.sln | 1 + 2 files changed, 884 insertions(+) create mode 100644 Enumerables-Technet/Article/article-en.md diff --git a/Enumerables-Technet/Article/article-en.md b/Enumerables-Technet/Article/article-en.md new file mode 100644 index 0000000..d92b816 --- /dev/null +++ b/Enumerables-Technet/Article/article-en.md @@ -0,0 +1,883 @@ +# Enumerable in deep + +This article discusses in details about the enumerables in C#. + +It based on french articles written [on this blog](http://www.yeg-projects.com/2015/01/soyons-enumerables-partie-1/) by Yan Grenier. + +All code samples are in the project "Enumerable-en" in the solution. + +# The enumerables + + +~~~ TO TRANSLATE + + + +Le principe des énumérables est de permettre de parcourir une liste d'éléments en récupérant élément par élément, a la manière d'un flux (on parle d'itération ou d'énumération). L'utilisation la plus courante de ce principe se fait avec le mot clé `foreach`. + +Dans .Net les énumérables fonctionnent via les interfaces `System.Collections.IEnumerable` et `System.Collections.IEnumerator`. Ces interfaces existent en version générique `System.Collections.Generic.IEnumerable<>` et `System.Collections.Generic.IEnumerator<>`. + +A partir du moment où une classe implémente l'interface `IEnumerable` alors elle devient énumérable. + +`IEnumerable` indique que l'on peut énumérer un objet et il a la charge de fournir un \ `IEnumerator` à chaque fois que l'on veut parcourir ses éléments. + +`IEnumerator` est "l'énumérateur", c'est à dire l'objet qui va se charger de "parcourir" les éléments à énumérer. + +Donc pour rendre une classe énumérable, on implémente `IEnumerable` qui va créer un `IEnumerator` qui aura la charge de l'itération des éléments. + +A savoir: toutes les listes, collections, dictionnaires, ainsi que les tableaux du .Net, implémentent `IEnumerable`. + +## Interface IEnumerable/IEnumerable<T> + +Cette interface indique qu'on peut énumérer l'objet de la classe qui l'implémente. Elle ne possède qu'une méthode `GetEnumerator()` retournant un `IEnumerator`. + +```csharp +public interface IEnumerable +{ + IEnumerator GetEnumerator(); +} +public interface IEnumerable : IEnumerable +{ + IEnumerator GetEnumerator(); +} +``` + +Le principe de cette interface et de fournir un nouvel objet énumérateur à chaque appel permettant ainsi de démarrer une nouvelle itération, et permettre également d'avoir plusieurs itérations en parallèle sur la même source (si la logique de l'objet sous-jacent le permet). + +## Interface IEnumerator/IEnumerator<T> + +Cette interface représente la logique d'itération (objet énumérateur). C'est elle qui indique l'élément en cours et qui permet de se "déplacer" dans l'énumération. + +```csharp +public interface IEnumerator +{ + object Current { get; } + bool MoveNext(); + void Reset(); +} +public interface IEnumerator : IDisposable, IEnumerator +{ + T Current { get; } +} +``` + +Le fonctionnement de l'énumérateur est assez simple : il indique l'élément en cours d'énumération (propriété `Current`), et se déplace vers le prochain élément avec la méthode `MoveNext()`. + +## Principe de l'itération + +Pour parcourir un énumérable on utilise toujours le même principe : + +- On demande à `IEnumerable` un nouvel énumérateur +- Tant que `IEnumerator.MoveNext()` retourne `true` + - On traite `IEnumerator.Current` + +C'est ce que fait l'instruction `foreach` pour nous. + +Ainsi la boucle suivante (méthode `ForEach()` dans le programme d'exemple) : + +```csharp +// Récupération de l'énumérable +IEnumerable enumerable = GetItems(); +// Parcours chaque élément dans 'enumerable' +foreach (int elm in enumerable) +{ + // .. +} +``` + +est approximativement compilée comme ceci (méthode `IterationBase()` dans le programme d'exemple) : + +```csharp +// Récupération de l'énumérable +IEnumerable enumerable = GetItems(); +// Récupère un nouvel énumérateur +IEnumerator enumerator = enumerable.GetEnumerator(); +// Tant que l'énumérateur se déplace +while (enumerator.MoveNext()) +{ + Int32 elm = enumerator.Current; + // .. +} +``` + +Toutefois ce n'est pas tout à fait exact, car la boucle `foreach` gère également la possibilité qu'un énumérateur soit disposable (implémentant `IDisposable`) (la méthode `ForEachDisposable()` le montre), donc la boucle équivalente est en réalité plus comme ceci (méthode `IterationBaseWithDispose()` dans le programme d'exemple) : + +```csharp +// Récupération de l'énumérable +IEnumerable enumerable = GetItems(true); +// Récupère un nouvel énumérateur +IEnumerator enumerator = enumerable.GetEnumerator(); +try +{ + // Tant que l'énumérateur se déplace + while (enumerator.MoveNext()) + { + Int32 elm = enumerator.Current; + // .. + Console.WriteLine(elm); + } +} +finally +{ + // On détermine si l'énumérateur est disposable + IDisposable disp = enumerator as IDisposable; + // Si c'est le cas on dispose l'énumérateur + if (disp != null) + { + disp.Dispose(); + } +} +``` + +De cette manière que la boucle s'arrête normalement, ou prématurément via un `break`, ou à cause d'une exception, notre énumérateur sera disposé. + +Il faut comprendre qu'implémenter `IDisposable` sur notre énumérateur est le seul moyen que nous avons pour déterminer qu'une itération est terminée, surtout si on a pas parcouru l'ensemble des éléments. + +Petit aparté : en réalité `foreach` ne prend pas en charge que des `IEnumerable`. Ce qui importe pour le compilateur lors d'un `foreach` c'est que la source à énumérer possède une méthode `GetEnumerator()` publique qui retourne un type qui possède une méthode `MoveNext()` retournant un booléen, et une propriété `Current`. Dans le programme d'exemple vous trouverez la méthode `ForEachWithoutIEnumerable()` utilisant les objets `FakeEnumerable` et `FakeEnumerator` n'implémentant pas les interfaces. + +## Les énumérateurs + +Pour faire un énumérateur il faut donc implémenter `IEnumerator`. Toutefois avant d'en implémenter un, si vous voulez énumérer un tableau privé (ou tout autre IEnumerable) par exemple, il vous suffit de renvoyer le `GetNumerator()` du tableau. + +L'implémentation est assez simple au final, ce qui importe c'est qu'à sa création l'énumérateur est dans un état "indéfini", c'est à dire qu'on ne s'est pas encore déplacé, donc `Current` doit se trouver avec une valeur par défaut, et on doit attendre le premier `MoveNext()` pour commencer vraiment notre itération. Ce qui implique que si vous avez des initialisations à faire (se connecter à une base de données par exemple) vous devez le faire lors du premier `MoveNext()` pour des raisons d'optimisation; on peut avoir besoin d'instancier un énumérateur sans pour autant le parcourir, ce qui peut être le cas quand on enchaîne plusieurs énumérables comme LINQ (cf prochaine partie de cet article). + +Imaginons que nous voulons créer un énumérateur qui parcours une liste à l'envers. La méthode `TestReverse()` du programme d'exemple montre l'utilisation de notre classe énumérateur. + +```csharp +/// +/// Enumérateur parcourant une liste dans le sens inverse +/// +public class ReverseEnumerator : IEnumerator +{ + IList _Source; + int _Position; + bool _Completed; + + /// + /// Création d'un nouvel énumérateur + /// + public ReverseEnumerator(IList source) + { + this._Source = source; + // On met -1 pour indiquer qu'on a pas commencé l'itération + this._Position = -1; + // L'itération n'est pas terminée + this._Completed = false; + // On défini Current avec la valeur par défaut + this.Current = default(T); + } + + /// + /// Libération des ressources + /// + public void Dispose() + { + // On a rien à libérer , mais on marque notre itérateur comme terminé + this._Completed = true; + } + + /// + /// Cette méthode est appelée lorsque l'on veut réinitialiser l'énumérateur + /// + public void Reset() + { + // On met -1 pour indiquer qu'on a pas commencer l'itération + this._Position = -1; + // L'itération n'est pas terminée + this._Completed = false; + // On défini Current avec la valeur par défaut + this.Current = default(T); + } + + /// + /// On se déplace vers le prochain élément + /// + /// False lorsque l'itération est terminée + public bool MoveNext() + { + // Si la source est Null alors on a rien à parcourir, donc l'itération s'arrête + if (this._Source == null) return false; + + // Si l'itération est terminée alors on ne va pas plus loin + if (this._Completed) return false; + + // Si la position est à -1 on récupère le nombre d'éléments à parcourir pour démarrer l'itération + if (this._Position == -1) + { + this._Position = _Source.Count; + } + + // On se déplace dans la liste + this._Position--; + + // Si on a atteind -1 alors on a terminé l'itération + if (this._Position < 0) + { + this._Completed = true; + return false; + } + + // On défini Current et on continue + Current = this._Source[this._Position]; + + return true; + } + + /// + /// Elément en cours de l'itération + /// + public T Current { get; private set; } + + /// + /// Elément en cours pour la version non générique + /// + object System.Collections.IEnumerator.Current + { + get { return Current; } + } + +} +``` + +Comme on peut le voir le principe de l'implémentation est assez simple. + +Toutefois ça peut vite se compliquer quand nous avons des énumérateurs complexes (parcours d'un arbre syntaxique par exemple). Nous verrons au chapitre suivant comment on peut se simplifier la vie. + +Dernier point pour cette partie : `IEnumerator` demande l'implémentation de `Reset()`. Il faut savoir que cette méthode n'est pas vraiment utilisée, la plupart du temps elle lève une exception `NotImplementedException`. Donc si son implémentation est trop complexe, n'ayez pas peur de faire de même. En fait on part du principe que si on veut redémarrer une itération on fait à nouveau appel à `IEnumerable.GetEnumerator()` qui va nous instancier un nouvel énumérateur, donc le reset n'a plus lieu d'être. + +# Les méthodes Yield + +Précédemment, certains d'entre vous ont dû se dire qu'à chaque fois que l'on veut mettre en place un énumérateur un peu particulier, il y a beaucoup de code à mettre place. + +Effectivement on peut vite se retrouver avec des énumérateurs complexes, avec interception d'erreurs, libération de ressources multiples, etc., ce qui peut rapidement compliquer les choses. + +C'est là que le compilateur C# va venir à notre rescousse via le mot clé yield (VB.NET supporte également une instruction `Yield`). + +Bien imaginons par exemple que nous voulons un énumérable auquel nous transmettons une liste de fichiers, et cet énumérable va parcourir chaque fichier et énumérer chaque ligne de texte du fichier. + +Cet exemple est intéressant car il inclus un gestion des erreurs (un fichier peut ne pas exister, ou être verrouillé, etc.) et une gestion des ressources multiples (chaque fichier doit être disposé). + +## Version brut de code + +Commençons par une version ressemblant typiquement à la solution que va prendre quelqu'un qui ne comprends pas les principes des énumérables ou tout simplement qui cherche à éviter la difficulté de faire un énumérateur correct. + +Cette solution se contente de lire tous les fichiers dans une liste, et lorsqu'on à besoin d'un énumérateur on retourne celui de la liste. (La méthode `TestFilesV1()` dans le programme exemple montre son utilisation). + +```csharp +/// +/// Enumérable parcourant les lignes texte d'un ensemble de fichier +/// +public class EnumFilesV1 : IEnumerable +{ + private List _Lines; + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV1(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + // On marque la liste des lignes à charger en la mettant à null + _Lines = null; + } + void LoadFiles() + { + // Création de la liste des lignes + _Lines = new List(); + if (this.Files != null) + { + // Pour chaque fichier + foreach (var file in Files) + { + try + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + _Lines.Add(line); + } + } + } + catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier + } + } + } + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // Si la liste des lignes est null alors il faut lire les fichiers + if (_Lines == null) + { + // Chargement des fichiers + LoadFiles(); + } + // Retourne l'énumérateur de la liste + return _Lines.GetEnumerator(); + } + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } +} +``` + +Alors qu'est-ce qui ne va pas avec ce code ? + +La première chose c'est que si les fichiers venaient à changer de contenu entre deux appels de `GetEnumerator()` nous retournerons à chaque fois le contenu de la lecture initiale. On peut résoudre ce problème simplement en modifiant légèrement notre code pour reconstruire la liste des lignes à chaque appel. + +```csharp +/// +/// Enumérable parcourant les lignes texte d'un ensemble de fichier reconstruisant une liste à chaque appel +/// +public class EnumFilesV2 : IEnumerable +{ + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV2(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + } + IList LoadFiles() + { + // Création de la liste des lignes + var result = new List(); + if (this.Files != null) + { + // Pour chaque fichier + foreach (var file in Files) + { + try + { + // Ouverture d'un lecteur de fichier texte + using (var reader = new StreamReader(file)) + { + // Lecture de chaque ligne du fichier + String line; + while ((line = reader.ReadLine()) != null) + { + // On ajoute la ligne dans la liste + result.Add(line); + } + } + } + catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier + } + } + // On retourne la liste + return result; + } + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // On construit la liste + var list = LoadFiles(); + // Retourne l'énumérateur de la liste + return list.GetEnumerator(); + } + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } +} +``` + +On supprime la liste des lignes de l'énumérable et on construit une nouvelle liste à chaque appel de `GetEnumerator()` et on retourne l'énumerateur de cette nouvelle liste. (La méthode TestFilesV2() dans le programme exemple montre son utilisation). + +Cette fois nous respectons un peu mieux les principes des énumérables toutefois ce n'est toujours pas satisfaisant d'un point de vue des performances. En effet s'il s'avère que les fichiers sont volumineux, nous chargeons tout dans une liste en mémoire. Imaginons que nous nous servons de cet énumérable pour filtrer quelques lignes sur un million, nous pouvons engorger notre mémoire inutilement. Pire si on extrait que les milles premières lignes, nous aurons chargé 999000 lignes de trop :( + +## Version plus subtile + +L'idéal serait de ne lire une ligne de texte que quand on en a besoin. Bref en gros lors du `IEnumerator.MoveNext()`. + +Nous allons donc faire preuve de subtilité et gérer la lecture en flux. + +```csharp +/// +/// Enumérable parcourant les lignes texte d'un ensemble de fichier via un énumérateur +/// +public class EnumFilesV3 : IEnumerable +{ + /// + /// Création d'un nouvel énumérable + /// + public EnumFilesV3(IEnumerable files) + { + // Initialisation des fichiers + this.Files = files.ToArray(); + } + /// + /// Retourne l'énumérateur des lignes + /// + public IEnumerator GetEnumerator() + { + // Retourne un nouvel énumérateur + return new FileEnumerator(Files); + } + /// + /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } +} +/// +/// Enumérateur de fichier +/// +class FileEnumerator : IEnumerator +{ + Func _CurrentState = null; + int _CurrentFilePos; + String _CurrentFileName; + TextReader _CurrentFile; + /// + /// Création d'un nouvel énumérateur + /// + public FileEnumerator(String[] files) + { + // Initialisation des fichiers + this.Files = files; + // Initialisation de l'énumérateur + Current = null; + _CurrentFilePos = 0; + _CurrentFileName = null; + _CurrentFile = null; + // L'état de l'énumérateur est à l'ouverture du prochain fichier à traiter + _CurrentState = OpenNextFileState; + } + /// + /// Libération des ressources éventuelles + /// + public void Dispose() + { + // Si on a un fichier encore d'ouvert on libère la mémoire + if (_CurrentFile != null) + { + _CurrentFile.Dispose(); + _CurrentFile = null; + } + // On défini l'état 'Completed' + _CurrentState = CompletedState; + } + /// + /// Essayes d'ouvrir le prochain fichier de la liste + /// + bool GetNextFile() + { + String filename = null; + TextReader file = null; + while (file == null && Files != null && _CurrentFilePos < Files.Length) + { + try + { + filename = Files[_CurrentFilePos++]; + file = new StreamReader(filename); + } + catch { } + } + // Si on a un fichier d'ouvert + if (file != null) + { + _CurrentFileName = filename; + _CurrentFile = file; + return true; + } + // Sinon on a rien trouvé + return false; + } + /// + /// Ouverture du prochain fichier + /// + bool OpenNextFileState() + { + // Si on a pas ou plus de fichier on arrête tout + if (!GetNextFile()) + { + Current = null; + // On passe à l'état 'Completed' + _CurrentState = CompletedState; + // On termine + return false; + } + // On passe à l'état ReadNextLine + _CurrentState = ReadNextLineState; + // On lit la première ligne + return _CurrentState(); + } + /// + /// On est en cours de lecture + /// + bool ReadNextLineState() + { + try + { + // On lit la prochaine ligne du fichier + String line = _CurrentFile.ReadLine(); + // Si la ligne n'est pas null on la traite + if (line != null) + { + Current = line; + return true; + } + // La ligne est null alors on a atteint la fin du fichier, on libère sa ressource + } + catch + { + // Si une erreur survient à la lecture on ferme le fichier en cours pour éviter les boucles infinies + } + // Libération des ressources pour passer au fichier suivant + _CurrentFile.Dispose(); + _CurrentFile = null; + _CurrentFileName = null; + // On passe à l'état 'OpenNextFile' + _CurrentState = OpenNextFileState; + // On traite le prochain état + return _CurrentState(); + } + /// + /// L'itération est terminée on retourne toujours false + /// + bool CompletedState() + { + return false; + } + /// + /// On ne s'occupe pas de cette méthode + /// + public void Reset() + { + throw new NotSupportedException(); + } + /// + /// On se déplace + /// + public bool MoveNext() + { + // Exécution de l'état en cours + return _CurrentState(); + } + /// + /// Liste des fichiers + /// + public String[] Files { get; private set; } + /// + /// Valeur en cours + /// + public string Current { get; private set; } + object System.Collections.IEnumerator.Current { get { return Current; } } +} +``` + +Donc cette fois la partie lecture est défini dans un énumérateur. Il s'agit d'une simple machine à états : 'OpenNextFile', 'ReadNextFile' et 'Completed'. + +On constate qu'il faut être vigilant à chaque endroit où une erreur peut survenir, ne pas oublier de libérer les ressources en fonction de différentes situations, etc. + +En revanche nous lisons nos fichiers ligne par ligne, par conséquent la charge mémoire est à son minimum. En cas de dispose on libère le fichier ouvert, et on se place sur l'état 'Completed' pour que l'énumérateur ne puisse plus rien faire. + +Pour gérer tout ce petit monde on a pas mal de ligne de code. + +Et pour tester tout ca (méthode `TestFilesV3()`) on utilise le code suivant : + +```csharp +private static void TestFilesV3() +{ + // Création de l'énumérable avec les fichiers de tests + var enumerable = new EnumFilesV3(GetTestFileNames()); + // On parcours l'énumérable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + // On parcours l'énumérable et provoque un arrêt prématuré + int i = 0; + foreach (var line in enumerable) + { + if (i++ >= 4) break; + Console.WriteLine(line); + } +} +``` + +Qui va parcourir une fois toutes les lignes de tous les fichiers, et une seconde fois mais uniquement les 4 premières lignes de la liste. + +## 'yield' à notre secours + +Donc nous avons deux situations : + +- soit nous faisons un code assez court facilement maintenable, mais qui risque de nous poser des problèmes techniques. +- soit nous faisons un code plus performant, mais qui est plus complexe, long et difficile à maintenir. + + +Et si on pouvait concilier les deux ? + +Par exemple : + +```csharp +/// +/// Ouvre un nouveau fichier ou retourne null si une erreur à lieu +/// +static StreamReader OpenFile(String file) +{ + try + { + return new StreamReader(file); + } + catch + { + return null; + } +} +static IEnumerable EnumFilesYield(IEnumerable files) +{ + if (files != null) + { + // Pour chaque fichier + foreach (var file in files) + { + // Ouverture d'un lecteur de fichier texte + using (var reader = OpenFile(file)) + { + // reader peut être null si une erreur à eu lieu + if (reader != null) + { + // Lecture de chaque ligne du fichier + String line; + do + { + // Lecture d'une ligne, si une erreur à lieu on arrête la boucle + try + { + line = reader.ReadLine(); + } + catch + { + break; + } + // On envoi la ligne d'énumérable + if (line != null) + yield return line; + } while (line != null);// Boucle tant qu'on a une ligne + } + } + } + } +} +private static void TestFilesYield() +{ + // Récupération d'un énumérable avec les fichiers de tests + var enumerable = EnumFilesYield(GetTestFileNames()); + // On parcours l'énumérable + foreach (var line in enumerable) + { + Console.WriteLine(line); + } + // On parcours l'énumérable et provoque un arrêt prématuré + int i = 0; + foreach (var line in enumerable) + { + if (i++ >= 4) break; + Console.WriteLine(line); + } +} +``` + +`TestFilesYield()` est une méthode qui lance deux boucles d'un énumérable renvoyé par la méthode `EnumFilesYield()`. + +C'est cette dernière qui nous intéresse, alors petite explication de code. On constate que cette méthode retourne un `IEnumerable`, en revanche elle ne renvoie jamais d'énumérable, a la place on a dans la double boucle de lecture de fichier une instruction `yield return line;` ou `line` est une `string`. + +Le `yield return` dans une méthode indique au compilateur que cette méthode est en fait un énumérateur et que chaque `yield return` renvoi un élément de cet énumérateur. Techniquement le compilateur va transformer cette méthode en un objet `IEnumerator` qui va simuler le code de la méthode. + +En plus du `yield return` il existe le `yield break` qui arrête l'énumérateur. + +Globalement le compilateur est capable de convertir la plupart du code en énumérateur, toutefois on ne peut pas utiliser `yield return` dans un try-catch (mais on le peut dans un try-finally). En revanche un `yield break` peut se trouver dans un try-catch mais pas un try-finally. + +C'est pour ça que notre code est un peu plus compliqué qu'une simple double boucle pour prendre en compte d'éventuelles erreurs. Mais malgré celà il reste toujours plus court et facile à maintenir que notre énumérateur précédent. + +L'énumérateur généré supporte le `IDisposable` par exemple dans notre cas si l'itération se termine en cours de lecture d'un fichier, comme il y a un `using`, le compilateur se chargera de créer le code nécessaire pour disposer la ressource se trouvant dans le `using`. De même que si dans une boucle `foreach` une exception est levée, et que l'on a un bloc `finally`qui englobe le `yield return` en cours alors ce bloc `finally` sera exécuté. + +Le mot clé `yield` peut être utilisé dans une méthode qui retourne `IEnumerable` mais également `IEnumerator` ce qui nous permet d'implémenter `IEnumerable.GetEnumerator()` par une méthode `yield`. + +Dernier point, l'énumérateur généré par le compilateur ne prend pas en charge `IEnumerator.Reset()` une exception `NotSupportedException` est levée. + +Pour plus d'informations sur `yield` et les itérateurs, voici quelques liens : + +- [Référence C# de yield](https://msdn.microsoft.com/fr-fr/library/9k7k7cf0.aspx) +- [Les itérateurs en C# et VB.net](https://msdn.microsoft.com/fr-fr/library/dscyy5s0.aspx) + + +## Pour en finir avec 'yield' + +L'utilisation de `yield` est très pratique, elle permet de gérer des scénarios complexes avec un code 'classique'. La seule vraie difficulté réside dans la gestion des exceptions, qui peut compliquer notre code, mais de manière générale notre code reste toujours plus simple. + +Le compilateur et le debuggeur de Visual Studio sont extrêment performants et vous permettent de faire du pas à pas dans l'énumérateur généré par `yield`, et ainsi de tracer exactement ce qu'il se passe dans l'énumérateur, même si le code dans la méthode yield est complexe, la trace suit parfaitement votre code. + +Le programme d'exemple contient les différents exemples donnés, plus quelques méthodes `yield` suplémentaires pour montrer qu'on peut faire des choses complexes. + +# LINQ et les énumérables à la chaîne + +LINQ est un language de requêtage intégré au language C# ou VB.Net, le propos de cette partie n'est pas d'expliquer LINQ en lui-même mais son comportement avec les énumérables dans la suite des deux précédentes parties. + +Pour plus d'informations sur LINQ je vous renvoie [à la documentation officielle](https://msdn.microsoft.com/fr-fr/library/bb397926.aspx). + +## Rappel + +Une requête LINQ ressemble à celà (méthode `TestLinqRequest()`) : + +```csharp +var query = from p in GetPersons() + where p.IsFamily + orderby p.Name + select p; +``` + +Cette requête n'est pas très compliquée : `GetPersons()` est une méthode qui retourne un `IEnumerable`, et notre requête en filtre les éléments dont le booléen `IsFamily` est à `true` et retourne le résultat trié par leur nom. + +Si on regarde de plus près la variable `query` (dans VisualStudio il suffit de survoler le mot clé **var** pour afficher une info-bulle informant du type déterminé par le compilateur) on constate qu'il s'agit d'un `IEnumerable`. + +En réalité il faut savoir que le compilateur va compiler cette requête sous la forme suivante (méthode `TestLinqRequestAsFluent()`) : + +```csharp +var query = GetPersons() + .Where(p => p.IsFamily) + .OrderBy(p => p.Name) + ; +``` + +LINQ fourni un ensemble de méthodes d'extensions (Select, Where, OrderBy, etc.) que l'on nomment "opérateur" car ils correspondent aux mot clés du language LINQ. + +Si on regarde de plus près les opérateurs de notre requête on constate qu'ils renvoient tous un `IEnumerable<>`. En fait chaque appel de ces opérateurs va créer un objet "proxy" de type énumérable qui encapsule l'énumérable "source". + +Ainsi notre variable `query` est en réalité une "chaîne" d'énumérables, elle contient un énumérable de type "OrderBy" qui se base sur un énumérable de type "Where" qui se base sur l'énumérable renvoyé par `GetPersons()`. + +## Mais alors, quel est le problème ? + +Voici une petite histoire vécue: un matin réception du mail suivant : + +> Je possède une table de données extrêmement volatile où je peux avoir une centaine de lignes insérées par seconde. +> +> Mon application affiche un tableau de bord avec des stats réactualisées régulièrement. Hors depuis que je suis en production LINQ calcul un nombre de lignes actualisées différents du nombre de lignes traitées, je ne comprends pas ! +> +> Voici mon code +> +> ```csharp +> class Stats { +> private DateTime lastRefresh; +> ... +> public void Refresh(){ +> var q = from stats in GetStats() +> where stat.Date >= lastRefresh +> orderby stat.Date +> select stats; +> +> foreach (var stats in q) +> { +> // Affichage de la stat +> } +> +> lblLastUpdate.Text = String.Format("{0} : {1} nouvelles statistiques", DateTime.Now, q.Count()); +> lastRefresh = DateTime.Now; +> } +> } +> ``` + +La source de données est un contexte Entity Framework, qui gère en réalité des `IQueryable` mais ces derniers sont des `IEnumerable`. + +Après discussion il s'avère que cette persone n'avait pas compris que sa requête `q` était un énumérable et que par conséquent chaque fois qu'il faisait une itération, un appel à GetEnumerator() était effectué et dans son cas une nouvelle requête SQL était exécutée. Hors l'opérateur `Count()` qu'il utilise pour déterminer le nombre de nouvelles statistiques ne renvoie pas un `IEnumerable` mais provoque une itération pour compter le nombre d'éléments. Seulement ses données étant tellement volatiles qu'entre les deux requêtes de nouvelles statistiques sont apparues. + +Le problème est là : en fait certaines personnes pensent qu'une requête LINQ est une sorte de liste tampon, qu'une fois qu'on a lancé une requête le résultat reste en mémoire, donc faire un `Count()` derrière se contente de compter le résultat. + +C'était la logique de cette personne, habituée à utiliser des principes de **Recordset** ou de **Dataset** pour les données, elle pensait qu'une requête LINQ était une sorte d'objet auto-alimenté contenant le résultat de sa requête. + +## Penser 'flux' + +Comme nous l'avons vu en début d'article, les énumérateurs sont une logique de "flux" ou on traite élément par élément. Dans le cadre d'une base de données chaque ligne étant retournée dés qu'elle est reçue du serveur. + +Nous avons vu également qu'un énumérable n'est qu'un fournisseur d'énumérateur, tant que l'on ne provoque pas l'énumération (appel à GetEnumerator()) nous n'avons pas de flux. + +Il a été également expliqué que LINQ était en fait une chaîne d'énumérable. Notre requête en début de cette partie pourrait ressembler à cela : + +```csharp +tatic IEnumerable SimulWhere() +{ + foreach (var p in GetPersons()) + { + if(p.IsFamily) + yield return p; + } +} +static IEnumerable SimulOrderBy() +{ + List result = new List(); + result.AddRange(SimulWhere()); + result.Sort((x, y) => String.Compare(x.Name, y.Name)); + foreach (var p in result) + yield return p; +} +static void TestSimulLinq() +{ + var query = SimulOrderBy(); + foreach (var p in query) + Console.WriteLine(p.Name); + foreach (var p in query.Take(2)) + Console.WriteLine(p.Name); +} +``` + +Dans la méthode `TestSimulLinq()` vous pouvez faire un pas à pas pour voir que tant qu'on n'entre pas dans un `foreach`, `SimulOrderBy()` n'est pas appelée. En revanche dans notre exemple elle est appelée deux fois car on a deux `foreach`. + +Donc il faut toujours penser nos requêtes en flux, qui se déclenchent à chaque fois que l'on provoque une itération. + +Alors une question se pose: comment savoir qu'une itération est déclenchée avec les opérateurs LINQ ? En bien c'est simple, il suffit de regarder le type de retour de l'opérateur, s'il renvoi un `IEnumerable` il ne provoque pas d'itération, mais renvoi un nouvel objet énumérable avec son propre énumérateur pour appliquer son opération sur le flux. + +Le langage LINQ intégré ne génère que des énumérables, par exemple `Count()` n'a pas d'équivalent LINQ, il faut utiliser la méthode, mais on peut coupler les deux : + +```csharp +int count = (from p in GetPersons() + where p.IsFamily + orderby p.Name + select p).Count(); +``` + +# Conclusion + +En espérant que cet article vous a apporté plus de clarté sur la logique des énumérables : + +- que `IEnumerable` ne contient pas de données, mais sert uniquement a créer un nouveau flux d'élément à chaque `GetEnumerator()` +- qu'une requête LINQ n'est pas un ensemble de données mais un énumérable +- qu'à chaque fois que vous avez un énumérable, il faut penser flux, quelque soit l'implémentation de cet énumérable + +# Voir Aussi + +- [Article d'origine](http://www.yeg-projects.com/2015/01/soyons-enumerables-partie-1/) +- [MSDN: Référence C# de yield](https://msdn.microsoft.com/fr-fr/library/9k7k7cf0.aspx) +- [MSDN: Les itérateurs en C# et VB.net](https://msdn.microsoft.com/fr-fr/library/dscyy5s0.aspx) +- [MSDN: Documentation officielle de LINQ](https://msdn.microsoft.com/fr-fr/library/bb397926.aspx) diff --git a/Enumerables-Technet/Source/Enumerables.sln b/Enumerables-Technet/Source/Enumerables.sln index df840d1..52521b0 100644 --- a/Enumerables-Technet/Source/Enumerables.sln +++ b/Enumerables-Technet/Source/Enumerables.sln @@ -9,6 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Enumerables-en", "Enumerabl EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Articles", "Articles", "{83ADC8F3-65D4-4FF8-8B52-9614700F9E86}" ProjectSection(SolutionItems) = preProject + ..\Article\article-en.md = ..\Article\article-en.md ..\Article\article-fr.md = ..\Article\article-fr.md EndProjectSection EndProject From 2b22c9245465d115d05847916cdb48da219b2251 Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Sun, 13 Mar 2016 12:12:01 +0100 Subject: [PATCH 10/11] Translate some parts of the article. --- Enumerables-Technet/Article/article-en.md | 85 ++++++++++++----------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/Enumerables-Technet/Article/article-en.md b/Enumerables-Technet/Article/article-en.md index d92b816..f71f50f 100644 --- a/Enumerables-Technet/Article/article-en.md +++ b/Enumerables-Technet/Article/article-en.md @@ -8,28 +8,23 @@ All code samples are in the project "Enumerable-en" in the solution. # The enumerables +The purpose of the enumerables is to browse through a list of elements by retreiving element by element like a stream (named as iteration or enumeration). The most common usage of this principle is done with the `foreach` keyword. -~~~ TO TRANSLATE - - - -Le principe des énumérables est de permettre de parcourir une liste d'éléments en récupérant élément par élément, a la manière d'un flux (on parle d'itération ou d'énumération). L'utilisation la plus courante de ce principe se fait avec le mot clé `foreach`. +In .Net the enumerables are based on the interfaces `System.Collections.IEnumerable` and `System.Collections.IEnumerator`. The generic version of these interfaces exist as `System.Collections.Generic.IEnumerable<>` and `System.Collections.Generic.IEnumerator<>`. -Dans .Net les énumérables fonctionnent via les interfaces `System.Collections.IEnumerable` et `System.Collections.IEnumerator`. Ces interfaces existent en version générique `System.Collections.Generic.IEnumerable<>` et `System.Collections.Generic.IEnumerator<>`. +When a class implements the interface `IEnumerable` then it becomes enumerable. -A partir du moment où une classe implémente l'interface `IEnumerable` alors elle devient énumérable. +`IEnumerable` indicates than we can enumerate an objet that is responsible to provide an `IEnumerator` each time we want to browse its elements. -`IEnumerable` indique que l'on peut énumérer un objet et il a la charge de fournir un \ `IEnumerator` à chaque fois que l'on veut parcourir ses éléments. +`IEnumerator` is the "enumerator", that is the object that will be in charge of "Browsing" the elements to enumerate. -`IEnumerator` est "l'énumérateur", c'est à dire l'objet qui va se charger de "parcourir" les éléments à énumérer. +So to make a class as enumerable, we implements `IEnumerable` which will create an `IEnumerator` which will be responsible of the iteration of the elements. -Donc pour rendre une classe énumérable, on implémente `IEnumerable` qui va créer un `IEnumerator` qui aura la charge de l'itération des éléments. +Notice: all lists, collections, dictionnaries, and arrays in .Net, implement `IEnumerable`. -A savoir: toutes les listes, collections, dictionnaires, ainsi que les tableaux du .Net, implémentent `IEnumerable`. +## IEnumerable/IEnumerable<T> interface -## Interface IEnumerable/IEnumerable<T> - -Cette interface indique qu'on peut énumérer l'objet de la classe qui l'implémente. Elle ne possède qu'une méthode `GetEnumerator()` retournant un `IEnumerator`. +This interface indicates we can enumerate the object of the class that implements it. It provides only one method `GetEnumerator()` returning an `IEnumerator`. ```csharp public interface IEnumerable @@ -42,11 +37,13 @@ public interface IEnumerable : IEnumerable } ``` -Le principe de cette interface et de fournir un nouvel objet énumérateur à chaque appel permettant ainsi de démarrer une nouvelle itération, et permettre également d'avoir plusieurs itérations en parallèle sur la même source (si la logique de l'objet sous-jacent le permet). +The purpose of the class is to provide a new enumerator object each time we need to start a new iteration, and permits to have several iterations in parallel on the same source (if the object logic permits it). + + +## IEnumerator/IEnumerator<T> interface -## Interface IEnumerator/IEnumerator<T> +This interface represents the iteration logic (enumerator object). This it wich indicates the current element and permits to "move" in the enumeration. -Cette interface représente la logique d'itération (objet énumérateur). C'est elle qui indique l'élément en cours et qui permet de se "déplacer" dans l'énumération. ```csharp public interface IEnumerator @@ -61,38 +58,38 @@ public interface IEnumerator : IDisposable, IEnumerator } ``` -Le fonctionnement de l'énumérateur est assez simple : il indique l'élément en cours d'énumération (propriété `Current`), et se déplace vers le prochain élément avec la méthode `MoveNext()`. +The operation of the enumerator is simple: it indicates the current element of the enumeration (`Current` property), and moves to the next element with the `MoveNext()` method. -## Principe de l'itération +## Iteration basics -Pour parcourir un énumérable on utilise toujours le même principe : +To navigate through an enumerable we use always the same principle: -- On demande à `IEnumerable` un nouvel énumérateur -- Tant que `IEnumerator.MoveNext()` retourne `true` - - On traite `IEnumerator.Current` +- Get a new enumerator from `IEnumerable` +- While `IEnumerator.MoveNext()` returns `true` + - Process `IEnumerator.Current` -C'est ce que fait l'instruction `foreach` pour nous. +This is what the statement `foreach` do for us. -Ainsi la boucle suivante (méthode `ForEach()` dans le programme d'exemple) : +So the next loop (`ForEach()` method in the sample program): ```csharp -// Récupération de l'énumérable +// Get the enumerable IEnumerable enumerable = GetItems(); -// Parcours chaque élément dans 'enumerable' +// Iterates each element in 'enumerable' foreach (int elm in enumerable) { // .. } ``` -est approximativement compilée comme ceci (méthode `IterationBase()` dans le programme d'exemple) : +is approximately compiled like this (`IterationBase()` method in the sample program): ```csharp -// Récupération de l'énumérable +// Get the enumerable IEnumerable enumerable = GetItems(); -// Récupère un nouvel énumérateur +// Get a new enumerator IEnumerator enumerator = enumerable.GetEnumerator(); -// Tant que l'énumérateur se déplace +// While the enumerator move while (enumerator.MoveNext()) { Int32 elm = enumerator.Current; @@ -100,16 +97,16 @@ while (enumerator.MoveNext()) } ``` -Toutefois ce n'est pas tout à fait exact, car la boucle `foreach` gère également la possibilité qu'un énumérateur soit disposable (implémentant `IDisposable`) (la méthode `ForEachDisposable()` le montre), donc la boucle équivalente est en réalité plus comme ceci (méthode `IterationBaseWithDispose()` dans le programme d'exemple) : +However this is not entirely exact, because the `foreach` loop handles the possibility than an enumerator be disposable (that implements `IDisposable`) (`ForEachDisposable()` method in the sample program show it), so in fact the the equivalent loop is more like this (`IterationBaseWithDispose()` method in the sample program) : ```csharp -// Récupération de l'énumérable +// Get the enumerable IEnumerable enumerable = GetItems(true); -// Récupère un nouvel énumérateur +// Get a new enumerator IEnumerator enumerator = enumerable.GetEnumerator(); try { - // Tant que l'énumérateur se déplace + // While the enumerator move while (enumerator.MoveNext()) { Int32 elm = enumerator.Current; @@ -119,9 +116,9 @@ try } finally { - // On détermine si l'énumérateur est disposable + // Check if the enumerator is disposable IDisposable disp = enumerator as IDisposable; - // Si c'est le cas on dispose l'énumérateur + // If true the we dispose it if (disp != null) { disp.Dispose(); @@ -129,18 +126,24 @@ finally } ``` -De cette manière que la boucle s'arrête normalement, ou prématurément via un `break`, ou à cause d'une exception, notre énumérateur sera disposé. +In this way that the loop terminates normally, or prematurely by a `break` statement, or by an exception, our enumerator will be disposed. -Il faut comprendre qu'implémenter `IDisposable` sur notre énumérateur est le seul moyen que nous avons pour déterminer qu'une itération est terminée, surtout si on a pas parcouru l'ensemble des éléments. +It must be understood that implement 'IDisposable' on our enumerator is the only way we have to determine that an iteration is finished, especially if it has not browsed all the elements. -Petit aparté : en réalité `foreach` ne prend pas en charge que des `IEnumerable`. Ce qui importe pour le compilateur lors d'un `foreach` c'est que la source à énumérer possède une méthode `GetEnumerator()` publique qui retourne un type qui possède une méthode `MoveNext()` retournant un booléen, et une propriété `Current`. Dans le programme d'exemple vous trouverez la méthode `ForEachWithoutIEnumerable()` utilisant les objets `FakeEnumerable` et `FakeEnumerator` n'implémentant pas les interfaces. +Small aside: in fact `foreach` do not supports only the `IEnumerable`. What the compiler needs the `foreach` statement, is that the source to enumerate provides a `GetEnumerator()` public method that returns a type that provides a `MoveNext()` public method returning a boolean, and a `Current` property. In the sample program you can find the `ForEachWithoutIEnumerable()` method using the `FakeEnumerable` and `FakeEnumerator` objects that does not implement the interfaces. ## Les énumérateurs -Pour faire un énumérateur il faut donc implémenter `IEnumerator`. Toutefois avant d'en implémenter un, si vous voulez énumérer un tableau privé (ou tout autre IEnumerable) par exemple, il vous suffit de renvoyer le `GetNumerator()` du tableau. +To make an enumerator we need to implements `IEnumerator`. However before you implement one, if you need iterate a private array (or another IEnumerable) for example, just return the array `GetNumerator()` method result. + L'implémentation est assez simple au final, ce qui importe c'est qu'à sa création l'énumérateur est dans un état "indéfini", c'est à dire qu'on ne s'est pas encore déplacé, donc `Current` doit se trouver avec une valeur par défaut, et on doit attendre le premier `MoveNext()` pour commencer vraiment notre itération. Ce qui implique que si vous avez des initialisations à faire (se connecter à une base de données par exemple) vous devez le faire lors du premier `MoveNext()` pour des raisons d'optimisation; on peut avoir besoin d'instancier un énumérateur sans pour autant le parcourir, ce qui peut être le cas quand on enchaîne plusieurs énumérables comme LINQ (cf prochaine partie de cet article). + +~~~ TO TRANSLATE + + + Imaginons que nous voulons créer un énumérateur qui parcours une liste à l'envers. La méthode `TestReverse()` du programme d'exemple montre l'utilisation de notre classe énumérateur. ```csharp From db8eb57f944641535425eef1deda36052b129d1b Mon Sep 17 00:00:00 2001 From: Yan Grenier Date: Sun, 13 Mar 2016 19:10:06 +0100 Subject: [PATCH 11/11] Translate more parts of the article. --- Enumerables-Technet/Article/article-en.md | 320 +++++++++--------- .../Source/Enumerables-en/EnumFilesV1.cs | 4 +- .../Source/Enumerables-en/EnumFilesV3.cs | 2 +- 3 files changed, 159 insertions(+), 167 deletions(-) diff --git a/Enumerables-Technet/Article/article-en.md b/Enumerables-Technet/Article/article-en.md index f71f50f..a25c683 100644 --- a/Enumerables-Technet/Article/article-en.md +++ b/Enumerables-Technet/Article/article-en.md @@ -132,23 +132,17 @@ It must be understood that implement 'IDisposable' on our enumerator is the only Small aside: in fact `foreach` do not supports only the `IEnumerable`. What the compiler needs the `foreach` statement, is that the source to enumerate provides a `GetEnumerator()` public method that returns a type that provides a `MoveNext()` public method returning a boolean, and a `Current` property. In the sample program you can find the `ForEachWithoutIEnumerable()` method using the `FakeEnumerable` and `FakeEnumerator` objects that does not implement the interfaces. -## Les énumérateurs +## The enumerators To make an enumerator we need to implements `IEnumerator`. However before you implement one, if you need iterate a private array (or another IEnumerable) for example, just return the array `GetNumerator()` method result. +The implementation is simple, but when the enumerator is created, it is in en "undefined" state, it means that it is not yet moved, therefore `Current` must returns a default value, and we need wait the first `MoveNext()` to start the iteration. Which means that if you have some initializations to do (like connect to a daatabase for example) you do it when the first `MoveNext()` optimisation reasons; we may need to instanciate an enumerator without enumerate it, which it may be the case when you link several LINQ queries (see next part of the article). -L'implémentation est assez simple au final, ce qui importe c'est qu'à sa création l'énumérateur est dans un état "indéfini", c'est à dire qu'on ne s'est pas encore déplacé, donc `Current` doit se trouver avec une valeur par défaut, et on doit attendre le premier `MoveNext()` pour commencer vraiment notre itération. Ce qui implique que si vous avez des initialisations à faire (se connecter à une base de données par exemple) vous devez le faire lors du premier `MoveNext()` pour des raisons d'optimisation; on peut avoir besoin d'instancier un énumérateur sans pour autant le parcourir, ce qui peut être le cas quand on enchaîne plusieurs énumérables comme LINQ (cf prochaine partie de cet article). - - -~~~ TO TRANSLATE - - - -Imaginons que nous voulons créer un énumérateur qui parcours une liste à l'envers. La méthode `TestReverse()` du programme d'exemple montre l'utilisation de notre classe énumérateur. +Suppose you want to create an enumerator to iterate a list in the reverse order. The `TestReverse()`method in the sample program show the use of our enumerator class. ```csharp /// -/// Enumérateur parcourant une liste dans le sens inverse +/// Enumerator iterates the list in the reverse way /// public class ReverseEnumerator : IEnumerator { @@ -157,82 +151,82 @@ public class ReverseEnumerator : IEnumerator bool _Completed; /// - /// Création d'un nouvel énumérateur + /// Create a new enumerator /// public ReverseEnumerator(IList source) { this._Source = source; - // On met -1 pour indiquer qu'on a pas commencé l'itération + // Set -1 to indicates the iteration is not started this._Position = -1; - // L'itération n'est pas terminée + // The iteration is not finished this._Completed = false; - // On défini Current avec la valeur par défaut + // Set the Current value by default this.Current = default(T); } /// - /// Libération des ressources + /// Release the resources /// public void Dispose() { - // On a rien à libérer , mais on marque notre itérateur comme terminé + // Nothing to dispose, but mark the iterator as finished this._Completed = true; } /// - /// Cette méthode est appelée lorsque l'on veut réinitialiser l'énumérateur + /// This method is called when we want to reset the enumerator /// public void Reset() { - // On met -1 pour indiquer qu'on a pas commencer l'itération + // Set -1 to indicates the iteration is not started this._Position = -1; - // L'itération n'est pas terminée + // The iteration is not finished this._Completed = false; - // On défini Current avec la valeur par défaut + // Set the Current value by default this.Current = default(T); } /// - /// On se déplace vers le prochain élément + /// We go to the next element /// - /// False lorsque l'itération est terminée + /// False when the iteration is finished public bool MoveNext() { - // Si la source est Null alors on a rien à parcourir, donc l'itération s'arrête + // If the source is null then we have nothing to browse, the iteration is finished if (this._Source == null) return false; - // Si l'itération est terminée alors on ne va pas plus loin + // If the iteration is finished, we stop here if (this._Completed) return false; - // Si la position est à -1 on récupère le nombre d'éléments à parcourir pour démarrer l'itération + // If the is -1 we get the count of the elements to iterates for starting the iteration if (this._Position == -1) { this._Position = _Source.Count; } - // On se déplace dans la liste + // We move on the list this._Position--; - // Si on a atteind -1 alors on a terminé l'itération + // If we reach the -1 position the iteration is finished if (this._Position < 0) { this._Completed = true; return false; } - // On défini Current et on continue + // We set Current and continue Current = this._Source[this._Position]; return true; } /// - /// Elément en cours de l'itération + /// Current element /// public T Current { get; private set; } /// - /// Elément en cours pour la version non générique + /// Current element for the non generic version /// object System.Collections.IEnumerator.Current { @@ -242,219 +236,216 @@ public class ReverseEnumerator : IEnumerator } ``` -Comme on peut le voir le principe de l'implémentation est assez simple. +As we can see, implement enumerator is simple. -Toutefois ça peut vite se compliquer quand nous avons des énumérateurs complexes (parcours d'un arbre syntaxique par exemple). Nous verrons au chapitre suivant comment on peut se simplifier la vie. +However it can quickly become complicated when we have complex enumerators (browse a syntaxic tree for example). We will see in next section how to simplify our code. -Dernier point pour cette partie : `IEnumerator` demande l'implémentation de `Reset()`. Il faut savoir que cette méthode n'est pas vraiment utilisée, la plupart du temps elle lève une exception `NotImplementedException`. Donc si son implémentation est trop complexe, n'ayez pas peur de faire de même. En fait on part du principe que si on veut redémarrer une itération on fait à nouveau appel à `IEnumerable.GetEnumerator()` qui va nous instancier un nouvel énumérateur, donc le reset n'a plus lieu d'être. +Last words for this part: `IEnumerator` require to implements the `Reset()` method. You need to this method is not really used, most of the time it throws a `NotImplementedException`. So if your implementation is too complex, you can throw this exception too instead of. In fact, now we consider that if we need to restart an iteration, we call again `IEnumerable.GetEnumerator()` to create a new enumerator, so the `Reset()`is not more useful. -# Les méthodes Yield -Précédemment, certains d'entre vous ont dû se dire qu'à chaque fois que l'on veut mettre en place un énumérateur un peu particulier, il y a beaucoup de code à mettre place. +# The Yield methods -Effectivement on peut vite se retrouver avec des énumérateurs complexes, avec interception d'erreurs, libération de ressources multiples, etc., ce qui peut rapidement compliquer les choses. +Previously, we see how to implement an enumerator, but probably you think it could be complicated to create a class each time we need a special enumerator. Actually it can quickly end up with complex enumerators, with catching errors, release of multiple resources, etc., which can quickly complicate matters. -C'est là que le compilateur C# va venir à notre rescousse via le mot clé yield (VB.NET supporte également une instruction `Yield`). +It is there the C# compiler come to our rescue with the `yield` keyword (VB.NET also supports a `Yield` statement). -Bien imaginons par exemple que nous voulons un énumérable auquel nous transmettons une liste de fichiers, et cet énumérable va parcourir chaque fichier et énumérer chaque ligne de texte du fichier. +For example we want an enumerable whereby we pass a list of files, and this enumerable iterate each file and read all the text lines of the file. -Cet exemple est intéressant car il inclus un gestion des erreurs (un fichier peut ne pas exister, ou être verrouillé, etc.) et une gestion des ressources multiples (chaque fichier doit être disposé). +This example is interesting because it included an error handling (a file may not exists, or be locked, etc.) and a multiple resources management (each file must be disposed). -## Version brut de code +## Classic code version -Commençons par une version ressemblant typiquement à la solution que va prendre quelqu'un qui ne comprends pas les principes des énumérables ou tout simplement qui cherche à éviter la difficulté de faire un énumérateur correct. +Let's start by a typically version code, like someone don't know the enumerable principle, or don't want to create an enumerator. -Cette solution se contente de lire tous les fichiers dans une liste, et lorsqu'on à besoin d'un énumérateur on retourne celui de la liste. (La méthode `TestFilesV1()` dans le programme exemple montre son utilisation). +This solution read all the files in a list, and when we want to enumerate them, we return the enumerator of the list (look the `TestFilesV1()` method in the sample program). ```csharp /// -/// Enumérable parcourant les lignes texte d'un ensemble de fichier +/// Enumerable enumerates the text lines of a set of files /// public class EnumFilesV1 : IEnumerable { private List _Lines; /// - /// Création d'un nouvel énumérable + /// Create a new enumerable /// public EnumFilesV1(IEnumerable files) { - // Initialisation des fichiers + // Init the files this.Files = files.ToArray(); - // On marque la liste des lignes à charger en la mettant à null + // Mark the lines list as 'to load' by setting it to null _Lines = null; } void LoadFiles() { - // Création de la liste des lignes + // Create the lines list _Lines = new List(); if (this.Files != null) { - // Pour chaque fichier + // For each file foreach (var file in Files) { try { - // Ouverture d'un lecteur de fichier texte + // Open a file text reader using (var reader = new StreamReader(file)) { - // Lecture de chaque ligne du fichier + // Read each line of the file String line; while ((line = reader.ReadLine()) != null) { - // On ajoute la ligne dans la liste + // Add the line to the list _Lines.Add(line); } } } - catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier + catch { } // When an error raised while reading the file, go to the next } } } /// - /// Retourne l'énumérateur des lignes + /// Returns the lines enumerator /// public IEnumerator GetEnumerator() { - // Si la liste des lignes est null alors il faut lire les fichiers + // If the lines list is null then read the files if (_Lines == null) { - // Chargement des fichiers + // Load the files LoadFiles(); } - // Retourne l'énumérateur de la liste + // Returns the list enumerator return _Lines.GetEnumerator(); } /// - /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// Implements IEnumerator.GetEnumerator() (non generic version) /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } /// - /// Liste des fichiers + /// List of the files /// public String[] Files { get; private set; } } ``` -Alors qu'est-ce qui ne va pas avec ce code ? +So what is wrong with this code ? -La première chose c'est que si les fichiers venaient à changer de contenu entre deux appels de `GetEnumerator()` nous retournerons à chaque fois le contenu de la lecture initiale. On peut résoudre ce problème simplement en modifiant légèrement notre code pour reconstruire la liste des lignes à chaque appel. +First, if some files have content changing between to calls of `GetEnumerator()` we return only the initial read. We can resolve this by changing our code by rebuild the text lines list on each call. ```csharp /// -/// Enumérable parcourant les lignes texte d'un ensemble de fichier reconstruisant une liste à chaque appel +/// Enumerable enumerates the text lines of a set of files, rebuilding the list on each call /// public class EnumFilesV2 : IEnumerable { /// - /// Création d'un nouvel énumérable + /// Create a new enumerable /// public EnumFilesV2(IEnumerable files) { - // Initialisation des fichiers + // Init the files this.Files = files.ToArray(); } IList LoadFiles() { - // Création de la liste des lignes + // Create the lines list var result = new List(); if (this.Files != null) { - // Pour chaque fichier + // For each file foreach (var file in Files) { try { - // Ouverture d'un lecteur de fichier texte + // Open a file text reader using (var reader = new StreamReader(file)) { - // Lecture de chaque ligne du fichier + // Read each line of the file String line; while ((line = reader.ReadLine()) != null) { - // On ajoute la ligne dans la liste + // Add the line of the list result.Add(line); } } } - catch { } // Si une erreur à la lecture du fichier on passe au prochain fichier + catch { } // When an error raised while reading the file, go to the next fichier } } - // On retourne la liste + // Returns the list return result; } /// - /// Retourne l'énumérateur des lignes + /// Returns the enumerator of the lines /// public IEnumerator GetEnumerator() { - // On construit la liste + // Build the list var list = LoadFiles(); - // Retourne l'énumérateur de la liste + // Return the list enumerator return list.GetEnumerator(); } /// - /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// Implements IEnumerator.GetEnumerator() (non generic version) /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } /// - /// Liste des fichiers + /// List of the files /// public String[] Files { get; private set; } } ``` -On supprime la liste des lignes de l'énumérable et on construit une nouvelle liste à chaque appel de `GetEnumerator()` et on retourne l'énumerateur de cette nouvelle liste. (La méthode TestFilesV2() dans le programme exemple montre son utilisation). - -Cette fois nous respectons un peu mieux les principes des énumérables toutefois ce n'est toujours pas satisfaisant d'un point de vue des performances. En effet s'il s'avère que les fichiers sont volumineux, nous chargeons tout dans une liste en mémoire. Imaginons que nous nous servons de cet énumérable pour filtrer quelques lignes sur un million, nous pouvons engorger notre mémoire inutilement. Pire si on extrait que les milles premières lignes, nous aurons chargé 999000 lignes de trop :( +We remove the list of text lines from the enumerable, and we build a new list on each call of `GetEnumerator()` and we return the enumerator of this new list (the `TestFilesV2()` method in the sample program shows the usage). -## Version plus subtile +Now we are in best respect of the enumerable principles, however is still not satisfy from a point of view performances. If we have some large files, we loading all lines in memory. If we have one million of lines and need to extract only the first thousand, we have loaded 999000 lines in memory for nothing :( -L'idéal serait de ne lire une ligne de texte que quand on en a besoin. Bref en gros lors du `IEnumerator.MoveNext()`. +## More optimized version -Nous allons donc faire preuve de subtilité et gérer la lecture en flux. +The best solution would be to read a text line only when it's needed. So each time we wall `IEnumerator.MoveNext()`. We will read as streaming. ```csharp /// -/// Enumérable parcourant les lignes texte d'un ensemble de fichier via un énumérateur +/// Enumerable enumerates the text lines of a set of files via an enumerator /// public class EnumFilesV3 : IEnumerable { /// - /// Création d'un nouvel énumérable + /// Create a new enumerable /// public EnumFilesV3(IEnumerable files) { - // Initialisation des fichiers + // Init the files this.Files = files.ToArray(); } /// - /// Retourne l'énumérateur des lignes + /// Returns the lines enumerator /// public IEnumerator GetEnumerator() { - // Retourne un nouvel énumérateur + // Returns a new enumerator return new FileEnumerator(Files); } /// - /// Implémentation de IEnumerator.GetEnumerator() (version non générique) + /// Implements IEnumerator.GetEnumerator() (non generic version) /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } /// - /// Liste des fichiers + /// List of the files /// public String[] Files { get; private set; } } /// -/// Enumérateur de fichier +/// File enumerator /// class FileEnumerator : IEnumerator { @@ -463,36 +454,36 @@ class FileEnumerator : IEnumerator String _CurrentFileName; TextReader _CurrentFile; /// - /// Création d'un nouvel énumérateur + /// Create a new enumerator /// public FileEnumerator(String[] files) { - // Initialisation des fichiers + // Init the files this.Files = files; - // Initialisation de l'énumérateur + // Init the enumerator Current = null; _CurrentFilePos = 0; _CurrentFileName = null; _CurrentFile = null; - // L'état de l'énumérateur est à l'ouverture du prochain fichier à traiter + // The enumerator state is to open the next file to read _CurrentState = OpenNextFileState; } /// - /// Libération des ressources éventuelles + /// Dispose some resources /// public void Dispose() { - // Si on a un fichier encore d'ouvert on libère la mémoire + // If we have a file opened we close it if (_CurrentFile != null) { _CurrentFile.Dispose(); _CurrentFile = null; } - // On défini l'état 'Completed' + // Set the state to 'Completed' _CurrentState = CompletedState; } /// - /// Essayes d'ouvrir le prochain fichier de la liste + /// Try to open the next file in the list /// bool GetNextFile() { @@ -507,120 +498,118 @@ class FileEnumerator : IEnumerator } catch { } } - // Si on a un fichier d'ouvert + // If we have a file opened if (file != null) { _CurrentFileName = filename; _CurrentFile = file; return true; } - // Sinon on a rien trouvé + // Else we don't found return false; } /// - /// Ouverture du prochain fichier + /// Open the next file /// bool OpenNextFileState() { - // Si on a pas ou plus de fichier on arrête tout + // If we don't have file, we stop all if (!GetNextFile()) { Current = null; - // On passe à l'état 'Completed' + // Go to the state 'Completed' _CurrentState = CompletedState; - // On termine + // We finished return false; } - // On passe à l'état ReadNextLine + // Go to the state ReadNextLine _CurrentState = ReadNextLineState; - // On lit la première ligne + // Read the first line return _CurrentState(); } /// - /// On est en cours de lecture + /// Read line state /// bool ReadNextLineState() { try { - // On lit la prochaine ligne du fichier + // Read the next line in the file String line = _CurrentFile.ReadLine(); - // Si la ligne n'est pas null on la traite + // If the line is not null we process it if (line != null) { Current = line; return true; } - // La ligne est null alors on a atteint la fin du fichier, on libère sa ressource + // The line is null so we reach the end of the file, we release the resource } catch { - // Si une erreur survient à la lecture on ferme le fichier en cours pour éviter les boucles infinies + // If an error raised while reading we close the current file to avoid infinite loops } - // Libération des ressources pour passer au fichier suivant + // Release resources to go to the next file _CurrentFile.Dispose(); _CurrentFile = null; _CurrentFileName = null; - // On passe à l'état 'OpenNextFile' + // Go to the state 'OpenNextFile' _CurrentState = OpenNextFileState; - // On traite le prochain état + // Process the next state return _CurrentState(); } /// - /// L'itération est terminée on retourne toujours false + /// The iteration is finished, so we returns always false /// bool CompletedState() { return false; } /// - /// On ne s'occupe pas de cette méthode + /// We don"t used this method /// public void Reset() { throw new NotSupportedException(); } /// - /// On se déplace + /// We move to the next line /// public bool MoveNext() { - // Exécution de l'état en cours + // Process the next state return _CurrentState(); } /// - /// Liste des fichiers + /// List of the files /// public String[] Files { get; private set; } /// - /// Valeur en cours + /// Current value /// public string Current { get; private set; } object System.Collections.IEnumerator.Current { get { return Current; } } } ``` -Donc cette fois la partie lecture est défini dans un énumérateur. Il s'agit d'une simple machine à états : 'OpenNextFile', 'ReadNextFile' et 'Completed'. +Now the reading part is defined in a enumerator. It a simple state machine: 'OpenNextFile', 'ReadNextFile' et 'Completed'. -On constate qu'il faut être vigilant à chaque endroit où une erreur peut survenir, ne pas oublier de libérer les ressources en fonction de différentes situations, etc. +We can see we need to be vigilant on every location where an error can occur, and don't forget to release the resources according differents situations, etc. -En revanche nous lisons nos fichiers ligne par ligne, par conséquent la charge mémoire est à son minimum. En cas de dispose on libère le fichier ouvert, et on se place sur l'état 'Completed' pour que l'énumérateur ne puisse plus rien faire. +We read our files text line by text line, so our memory is on a minimal usage. In case of dispose we release the opened file, and we set the state to 'Completed' and the enumerator can't do nothing. -Pour gérer tout ce petit monde on a pas mal de ligne de code. - -Et pour tester tout ca (méthode `TestFilesV3()`) on utilise le code suivant : +We do lot of code for a little work, and for test it, you can look the `TestFilesV3()` method in the sample program: ```csharp private static void TestFilesV3() { - // Création de l'énumérable avec les fichiers de tests + // Create the enumerable with tests files var enumerable = new EnumFilesV3(GetTestFileNames()); - // On parcours l'énumérable + // Iterates the enumerable foreach (var line in enumerable) { Console.WriteLine(line); } - // On parcours l'énumérable et provoque un arrêt prématuré + // Iterates the enumerable and force a break int i = 0; foreach (var line in enumerable) { @@ -630,23 +619,22 @@ private static void TestFilesV3() } ``` -Qui va parcourir une fois toutes les lignes de tous les fichiers, et une seconde fois mais uniquement les 4 premières lignes de la liste. +This example read all the lines in a first loop, and in a second loop it read only the first 4 lines. -## 'yield' à notre secours +## 'yield' in our rescue -Donc nous avons deux situations : +So we have to situations: -- soit nous faisons un code assez court facilement maintenable, mais qui risque de nous poser des problèmes techniques. -- soit nous faisons un code plus performant, mais qui est plus complexe, long et difficile à maintenir. +- either we create a small code, easily maintainable, but with some performances risks. +- either we create a more efficient code, but wich is more complex, long and sometimes difficult to maintain. - -Et si on pouvait concilier les deux ? +And if we can reconcile the both ? -Par exemple : +For example: ```csharp /// -/// Ouvre un nouveau fichier ou retourne null si une erreur à lieu +/// Open a new file or null if an error raised /// static StreamReader OpenFile(String file) { @@ -663,20 +651,20 @@ static IEnumerable EnumFilesYield(IEnumerable files) { if (files != null) { - // Pour chaque fichier + // For each files foreach (var file in files) { - // Ouverture d'un lecteur de fichier texte + // Open a file text reader using (var reader = OpenFile(file)) { - // reader peut être null si une erreur à eu lieu + // reader can be null if an error raised if (reader != null) { - // Lecture de chaque ligne du fichier + // Read each line of the file String line; do { - // Lecture d'une ligne, si une erreur à lieu on arrête la boucle + // Read the line, if an error raised we stop the loop try { line = reader.ReadLine(); @@ -685,10 +673,10 @@ static IEnumerable EnumFilesYield(IEnumerable files) { break; } - // On envoi la ligne d'énumérable + // Returns the line in the 'enumerable' if (line != null) yield return line; - } while (line != null);// Boucle tant qu'on a une ligne + } while (line != null);// Loop while we have a line } } } @@ -696,14 +684,14 @@ static IEnumerable EnumFilesYield(IEnumerable files) } private static void TestFilesYield() { - // Récupération d'un énumérable avec les fichiers de tests + // Get an enumerable with the files test var enumerable = EnumFilesYield(GetTestFileNames()); - // On parcours l'énumérable + // Iterates the enumerable foreach (var line in enumerable) { Console.WriteLine(line); } - // On parcours l'énumérable et provoque un arrêt prématuré + // Iterates the enumerable and force a break int i = 0; foreach (var line in enumerable) { @@ -713,39 +701,43 @@ private static void TestFilesYield() } ``` -`TestFilesYield()` est une méthode qui lance deux boucles d'un énumérable renvoyé par la méthode `EnumFilesYield()`. +`TestFilesYield()` is a method which run two loops for a enumerable returns par the `EnumFilesYield()` method. + +It this last method that interest us, so little explanation of code. This method returns an `IEnumerable`, however it never returns an enumerable, instead of we have in the two loops a statement `yield return line;` where `line` is a `string`. -C'est cette dernière qui nous intéresse, alors petite explication de code. On constate que cette méthode retourne un `IEnumerable`, en revanche elle ne renvoie jamais d'énumérable, a la place on a dans la double boucle de lecture de fichier une instruction `yield return line;` ou `line` est une `string`. +The `yield return` in the method indicates to the compiler that method is an enumerator and each `yield return` returns an element of this enumerator. Technically the compiler will transforms this method in a `IEnumerator` object wich simulates the method code. -Le `yield return` dans une méthode indique au compilateur que cette méthode est en fait un énumérateur et que chaque `yield return` renvoi un élément de cet énumérateur. Techniquement le compilateur va transformer cette méthode en un objet `IEnumerator` qui va simuler le code de la méthode. +In addition to the `yield return` there is a `yield break` that stop the enumerator. -En plus du `yield return` il existe le `yield break` qui arrête l'énumérateur. +Overall the compiler can convert the most code to an enumerator, however we can't use `yield return` in a try-catch (but it's possible in a try-finally). And a `yield break` can be used in a try-catch but not in a try-finally. -Globalement le compilateur est capable de convertir la plupart du code en énumérateur, toutefois on ne peut pas utiliser `yield return` dans un try-catch (mais on le peut dans un try-finally). En revanche un `yield break` peut se trouver dans un try-catch mais pas un try-finally. +It's for that our code is more complex than a simple double loop to handle possible errors. But despite this there is still more short and easy to maintain than our previous enumerator. -C'est pour ça que notre code est un peu plus compliqué qu'une simple double boucle pour prendre en compte d'éventuelles erreurs. Mais malgré celà il reste toujours plus court et facile à maintenir que notre énumérateur précédent. +The generated enumerator supports `IDisposable`, for example in our case if the iteration is stopped when reading a file, and because there is a `using`, the compiler create the code dispose the resource in the `using`. Same as if in a `foreach` an exception is raised, and the `yield return` is within a `try-finally` this block will be executed. -L'énumérateur généré supporte le `IDisposable` par exemple dans notre cas si l'itération se termine en cours de lecture d'un fichier, comme il y a un `using`, le compilateur se chargera de créer le code nécessaire pour disposer la ressource se trouvant dans le `using`. De même que si dans une boucle `foreach` une exception est levée, et que l'on a un bloc `finally`qui englobe le `yield return` en cours alors ce bloc `finally` sera exécuté. +The `yield` keyword can be used in a method that returns an `IEnumerable` or an `IEnumerator`, permitting to implements the `IEnumerable.GetEnumerator()` with a `yield` method. -Le mot clé `yield` peut être utilisé dans une méthode qui retourne `IEnumerable` mais également `IEnumerator` ce qui nous permet d'implémenter `IEnumerable.GetEnumerator()` par une méthode `yield`. +Last point; the enumerator generated by the compiler don't implements the `IEnumerator.Reset()`, an `NotSupportedException` will be raised. -Dernier point, l'énumérateur généré par le compilateur ne prend pas en charge `IEnumerator.Reset()` une exception `NotSupportedException` est levée. +For more informations about the `yield` and enumerable, look this links : -Pour plus d'informations sur `yield` et les itérateurs, voici quelques liens : +- [yield : C# reference](https://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx) +- [Iterators (C# and VB.Net)](https://msdn.microsoft.com/en-us/library/dscyy5s0.aspx) -- [Référence C# de yield](https://msdn.microsoft.com/fr-fr/library/9k7k7cf0.aspx) -- [Les itérateurs en C# et VB.net](https://msdn.microsoft.com/fr-fr/library/dscyy5s0.aspx) +## The last word about 'yield' +The keyword `yield` is very useful, it permits to handle some complex scenarios with a 'classic' code. The real difficulty is about the exceptions management, wich can complicate our code, but in general our code is always mor simple the create an enumerator. -## Pour en finir avec 'yield' +The Visual Studio compiler and debugger are very efficients by permitting to debug step-by-step within the generated enumerator by `yield`, and so tracing exactly what happens in the enumerator, event if the code in the yield method is complex, the trace follow exactly the code. -L'utilisation de `yield` est très pratique, elle permet de gérer des scénarios complexes avec un code 'classique'. La seule vraie difficulté réside dans la gestion des exceptions, qui peut compliquer notre code, mais de manière générale notre code reste toujours plus simple. +The sample program provides the examples discussed in this part, and more other `yield` methods to show some complex things. -Le compilateur et le debuggeur de Visual Studio sont extrêment performants et vous permettent de faire du pas à pas dans l'énumérateur généré par `yield`, et ainsi de tracer exactement ce qu'il se passe dans l'énumérateur, même si le code dans la méthode yield est complexe, la trace suit parfaitement votre code. +# LINQ and the chaining enumerables -Le programme d'exemple contient les différents exemples donnés, plus quelques méthodes `yield` suplémentaires pour montrer qu'on peut faire des choses complexes. -# LINQ et les énumérables à la chaîne + +~~~ TO TRANSLATE + LINQ est un language de requêtage intégré au language C# ou VB.Net, le propos de cette partie n'est pas d'expliquer LINQ en lui-même mais son comportement avec les énumérables dans la suite des deux précédentes parties. diff --git a/Enumerables-Technet/Source/Enumerables-en/EnumFilesV1.cs b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV1.cs index c257a2c..970543f 100644 --- a/Enumerables-Technet/Source/Enumerables-en/EnumFilesV1.cs +++ b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV1.cs @@ -16,7 +16,7 @@ public class EnumFilesV1 : IEnumerable private List _Lines; /// - /// Create a new enumerabl + /// Create a new enumerable /// public EnumFilesV1(IEnumerable files) { @@ -57,7 +57,7 @@ void LoadFiles() } /// - /// Returns the lines enumertor + /// Returns the lines enumerator /// public IEnumerator GetEnumerator() { diff --git a/Enumerables-Technet/Source/Enumerables-en/EnumFilesV3.cs b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV3.cs index cc27334..d8a6e49 100644 --- a/Enumerables-Technet/Source/Enumerables-en/EnumFilesV3.cs +++ b/Enumerables-Technet/Source/Enumerables-en/EnumFilesV3.cs @@ -194,7 +194,7 @@ public bool MoveNext() } /// - /// List odf the files + /// List of the files /// public String[] Files { get; private set; }