1. Introduction
On se rappelle la portée
Imagine que les variables, c’est des employés dans un grand bureau, et que les méthodes, les boucles et les blocs de code, c’est des pièces et des bureaux. Certains employés n’ont accès qu’à leur pièce, d’autres peuvent aller partout dans le bâtiment. Là où chacun peut aller — c’est ça leur portée (scope).
La portée détermine où une variable déclarée est “visible” dans le programme et où tu peux l’utiliser.
Les principaux types de portée
En C#, tu peux distinguer ces principaux types de portée :
| Portée | Exemple | Où la variable est "visible" |
|---|---|---|
| Locale | À l’intérieur d’une méthode ou d’un bloc | Seulement dans ce bloc |
| Paramètre de méthode | Dans la signature de la méthode | Seulement dans la méthode |
| Variable de classe (champ) | Dans le corps de la classe, hors des méthodes | Dans toutes les méthodes de cette classe |
| Variable dans une boucle/condition | À l’intérieur de d’une boucle/if |
Seulement dans ces |
Exemple avec explications
public class Bureau
{
int numeroBatiment = 50; // Champ de classe : visible dans toutes les méthodes public void AfficherInfo() {
int numeroPiece = 101; // Variable locale : visible seulement dans AfficherInfo if (numeroPiece > 100) {
int numeroBureau = 5; // Visible seulement dans ce bloc if Console.WriteLine(numeroBureau);
} Console.WriteLine(numeroBureau); //
Erreur ! numeroBureau n’est plus visible ici
}
}
2. Fonctions locales et portée
Qui voit qui ?
Quand tu déclares une fonction locale dans une méthode (ou même dans une boucle ou une condition), elle se retrouve dans la même portée que les variables déclarées au-dessus. Une fonction locale, c’est comme si elle faisait partie de la même pièce.
Exemple
La fonction locale voit les variables de la portée environnante
void AfficherAvecPrefixe(string message)
{
string
prefixe = "[LOG] : "; void Afficher() {
Console.WriteLine(
prefixe +
message); // voit les deux variables !
} Afficher();
}
Ici, les variables prefixe et message sont visibles dans la fonction locale Afficher, parce qu’elles sont déclarées dans la même portée ou une portée plus large.
Combien de portées ici ?
Dans l’exemple ci-dessus :
- il y a la portée de la méthode AfficherAvecPrefixe
- et à l’intérieur, la portée de la fonction Afficher
3. Capture de variables (Capture)
La capture de variables — c’est quand une fonction locale utilise des variables qui ont été déclarées en dehors de cette fonction, mais dans la même portée.
Les fonctions locales et les méthodes anonymes (lambda expressions, on en parlera plus tard) se rappellent (ou “capturent”) toutes les variables qui étaient accessibles au moment de leur déclaration.
On peut dire que les fonctions font comme une photo (capture) du monde autour — et peuvent utiliser ces variables même si elles sont appelées beaucoup plus tard.
Schéma
Méthode Main
└─ variable x
└─ fonction locale F() ← "capture" x
Exemple — capture toute simple
void ExempleCompteur()
{
int compteur = 0;
void Augmenter()
{
compteur++; // Cette fonction capture la variable compteur
}
Augmenter();
Augmenter();
Console.WriteLine(compteur); // Affichera 2
}
Ici, après deux appels de la fonction locale Augmenter, la valeur de compteur passe à 2.
4. Utilisation de la capture de variables
La capture de variables permet de “passer” facilement des données entre la portée de la méthode et les fonctions locales, sans se prendre la tête avec des paramètres en trop.
S’il n’y avait pas de capture, tu serais obligé de passer toutes les variables comme paramètres :
void ExempleCompteurSansCapture()
{
int compteur = 0;
void Augmenter(ref int c)
{
c++;
}
Augmenter(ref compteur);
Augmenter(ref compteur);
Console.WriteLine(compteur);
}
C’est pas pratique — pourquoi s’embêter à écrire ref et pourrir la signature de la fonction, alors qu’elle peut juste “voir” les variables de dehors ?
5. Fonctions locales et vie des variables après la sortie de la méthode
Les variables vivent-elles plus longtemps que la méthode ?
Si tu passes une fonction locale (ou un delegate avec une lambda) à l’extérieur de la méthode courante, les variables capturées ne “meurent” plus à la sortie de la méthode. Le CLR (la machine virtuelle .NET) s’en occupe — tout ce qu’il faut sera “retenu” en mémoire.
Exemple : les fonctions vivent hors de la méthode
Func<int> ObtenirCompteur()
{
int compte = 0;
int Incrementer()
{
compte++;
return compte;
}
return Incrementer; // On retourne la fonction dehors !
}
var compteur = ObtenirCompteur();
Console.WriteLine(compteur()); // 1
Console.WriteLine(compteur()); // 2
Ici, même après la fin de la méthode ObtenirCompteur, la variable compte continue d’exister, parce que la fonction retournée l’a capturée. C’est ce qu’on appelle une closure — on en reparlera dans une autre leçon, mais pour les fonctions locales, le mécanisme est le même.
6. Erreurs classiques et scénarios marrants
Réécriture de la variable avant l’appel de la fonction locale
Parfois, tu peux tomber sur le cas où la variable capturée par la fonction locale est modifiée avant son appel — et du coup le résultat peut être différent de ce que tu attendais.
Exemple :
void Exemple()
{
int x = 42;
void AfficherX() { Console.WriteLine(x); }
x = 100; // On a modifié la variable !
AfficherX(); // Affichera 100, pas 42 !
}
Astuce : La fonction locale voit toujours la dernière valeur de la variable au moment de l’appel.
Capture de variables dans une boucle for/foreach (encore une fois)
Le classique qui fait mal : si tu codes un algo en entretien ou sur un gros projet, vérifie toujours : est-ce que je capture pas une variable “vivante” de la boucle, et comment elle va se comporter.
GO TO FULL VERSION