45Fermer47
ZephLe 21/05/2016 à 11:49
Libération des ressources : la mémoire gérée par .NET est effectivement libérée automatiquement, mais ça n'est pas forcément le cas quand tu utilises des ressources système. Par exemple si tu ouvres un fichier, il faudra quand même le fermer à un moment. Bon, les classes sont toutes bien fichues, et quand le garbage collector va passer pour libérer ton instance de "File" il va appeler sa méthode "Dispose" qui va s'occuper de fermer le fichier, donc tout finira par être nettoyé correctement. Mais si tu as une exception dans ton programme c'est quand même plus propre de fermer le fichier immédiatement plutôt que d'attendre que le garbage collector le fasse pour toi.

Par convention, toutes les classes qui utilisent des ressources système (et qu'il faut donc prendre soin de libérer explicitement dès que possible) implémente l'interface "IDisposable". Elle t'impose d'implémenter une méthode "Dispose", qui est responsable de nettoyer toute ce qui doit l'être. Par convention également, un objet devient inutilisable à partir du moment où sa méthode "Dispose" est appelée.

Tu as d'ailleurs une construction pratique qui est prévue dans ce cas : le bloc "using". Il s'occupe d'appeler pour toi la méthode "Dispose" d'une classe "IDisposable" dès que tu en sors, quelle que soit la raison (return, exception, break, peu importe). Ça s'utilise comme ça :private bool Test() { using (var file = new FileStream("C:\fichier.txt", FileMode.Open)) { if (condition) return false; // file.Dispose() va être appelé juste avant de quitter la fonction ici if (autre_condition) throw new Exception("pwet"); // Idem ici // Si tout s'est bien passé jusqu'ici, la méthode file.Dispose() va être également appelée avant de quitter le bloc "using" } }Utiliser la construction "using" quand tu le peux dès que tu utilises des classes qui implémentent "IDisposable" permet de te simplifier beaucoup la vie en évitant de gérer toi-même tous les cas de sortie possibles.

Yield : c'est un peu plus compliqué à expliquer, il s'agit d'une syntaxe que te propose C# pour écrire des générateurs (mais tu pourrais les écrire à la main également, c'est juste plus long). Un générateur va te générer une suite d'objets, un à un, de façon à ce que tu puisses les énumérer dans une boucle foreach (par exemple). L'intérêt c'est que ces objets sont générés au fur et à mesure que la boucle se déroule, ce qui présente plusieurs avantages. Prenons cet exemple un peu débile (j'ai pas mieux désolé) :private IEnumerable<Resultat> CalculeResultats(int combien) { var results = Resultat[combien]; for (int i = 0; i < combien; ++i) resultat[i] = new Resultat(i); // Supposons que construire "Resultat(i)" est assez couteux, et qu'on veuille éviter de le faire pour rien return resultats; } foreach (var resultat in CalculeResultats(1000)) // Utilise résultat Avec ce bout de code, on va calculer les 1000 résultats avant de pouvoir commencer à les utiliser. Peut-être que ça serait plus intéressant si je pouvais plutôt utiliser les résultats au fur et à mesure que je les calcule, et ça tombe bien, le mot-clé "yield" permet de faire exactement ça :private IEnumerable<Resultat> CalculeResultats(int combien) { for (int i = 0; i < combien; ++i) yield return new Resultat(i); // Supposons que construire "Resultat(i)" est assez couteux, et qu'on veuille éviter de le faire pour rien } foreach (var resultat in CalculeResultats(1000)) // Utilise résultat Avec cette variante, le code compilé est assez différent. À chaque fois qu'un résultat est nécessaire (donc à chaque tour de boucle), ma fonction "CalculeResultats" va être appelée mais va s'arrêter après le "yield return", et me donner juste le résultat suivant pour pouvoir l'utiliser immédiatement. Cette alternance "calcule le résultat suivant / continue l'exécution de la boucle foreach" va continuer jusqu'à ce que "CalculeResultats" ait retourné tous ses résultats.

Attributs : mon message est déjà trop long et je ne suis pas un grand fan de cette fonctionnalité, donc je laisse à qqun d'autre le soin de répondre ^^