Programmation fonctionnelle en C# : Une approche moderne du développement
La programmation fonctionnelle (PF) est un paradigme dans lequel le calcul est traité comme l'évaluation de fonctions mathématiques et évite les changements d'état et les données mutables. Bien que C# ait été initialement créé comme un langage orienté objet, à partir de la version 3.0, il incorpore activement des éléments de l'approche fonctionnelle. Aujourd'hui, la programmation fonctionnelle en C# n'est pas seulement une mode passagère, mais un outil puissant pour écrire un code plus prévisible, plus testable et plus sûr pour la concurrence.
Dans cet article, nous allons décomposer les concepts clés de la PF appliqués à C# : les fonctions pures, l'immutabilité des données, LINQ, les fermetures (closures) et les expressions lambda. Vous verrez comment ces techniques aident à écrire un code concis et fiable dans l'écosystème .NET.
1. Fonctions pures et absence d'effets secondaires
Une fonction pure est une fonction qui :
- Retourne toujours le même résultat pour les mêmes arguments (transparence référentielle).
- N'a pas d'effets secondaires (ne modifie pas les variables globales, n'effectue pas d'entrée/sortie, ne modifie pas les arguments passés).
En C#, une fonction pure pourrait ressembler à ceci :
// Fonction purepublic int AddPure(int a, int b){ return a + b;}
// PAS une fonction pure (effet secondaire — sortie console et modification d'une variable globale)private int _state = 0;public int AddImpure(int a, int b){ _state++; Console.WriteLine("Appel de fonction"); return a + b + _state;}Les avantages des fonctions pures sont évidents : elles sont faciles à tester, prévisibles et peuvent être appelées en toute sécurité depuis des threads parallèles. Essayez d'extraire toute logique non liée aux E/S dans des fonctions pures.
2. Immutabilité des données
La programmation fonctionnelle encourage le travail avec des données immuables. Au lieu de modifier un objet existant, vous créez un nouvel objet avec des propriétés modifiées. En C#, les éléments suivants sont excellents pour cela :
- record (à partir de C# 9) — un type référence avec une égalité basée sur la valeur et une immutabilité par défaut.
- struct — pour les petites structures immuables (par exemple, coordonnées, argent).
- IEnumerable<T> — pour les collections immuables via LINQ.
Exemple avec record :
public record Person(string Name, int Age);
// Utilisation d'une expression with pour créer une copie avec modificationsvar original = new Person("Alexey", 30);var updated = original with { Age = 31 };
Console.WriteLine(original); // Person { Name = Alexey, Age = 30 }Console.WriteLine(updated); // Person { Name = Alexey, Age = 31 }Cette approche empêche la modification accidentelle des données à un endroit d'affecter d'autres parties du programme.
3. LINQ — Requêtes de données fonctionnelles
LINQ (Language Integrated Query) est peut-être l'exemple le plus frappant de l'approche fonctionnelle en C#. Il permet de traiter des collections de manière déclarative en utilisant des chaînes de méthodes.
Méthodes LINQ fonctionnelles clés :
Select— transformation de chaque élément (analogue à map).Where— filtrage (analogue à filter).Aggregate— pliage (analogue à reduce).SelectMany— aplatissement de collections imbriquées (flatMap).
Exemple : trouver la somme des carrés des nombres pairs.
int[] numbers = { 1, 2, 3, 4, 5, 6 };
// Style impératifint sumImperative = 0;foreach (var num in numbers){ if (num % 2 == 0) { sumImperative += num * num; }}
// Style fonctionnel avec LINQint sumFunctional = numbers .Where(n => n % 2 == 0) // filtrage .Select(n => n * n) // transformation .Sum(); // agrégation
C