Funktionale Programmierung in C#: Fortgeschrittenes Niveau
Funktionale Programmierung (FP) ist ein Paradigma, das in den letzten Jahren aktiv in die Welt von C# Einzug gehalten hat. Dank der Möglichkeiten von .NET können Entwickler objektorientierte und funktionale Ansätze kombinieren und so zuverlässigeren, testbareren und vorhersagbareren Code erstellen. In diesem Artikel werden wir fortgeschrittene FP-Techniken in C# behandeln, die Ihnen helfen, über die einfache Verwendung von LINQ hinauszugehen.
1. Reine Funktionen und Seiteneffekte: Strenge Kontrolle
Eine reine Funktion ist eine Funktion, deren Ergebnis nur von den Eingabeargumenten abhängt und die keine Seiteneffekte verursacht (Änderung des globalen Zustands, Ein-/Ausgabe, Ausnahmen). In C# können wir uns diesem Ideal annähern, indem wir unveränderliche Typen verwenden und die Änderung von Parametern vermeiden.
// Reine Funktion: Ergebnis hängt nur von x und y abpublic static int Add(int x, int y) => x + y;
// Unreine Funktion: hängt vom externen Zustand abprivate static int _counter = 0;public static int IncrementCounter() => ++_counter;Auf fortgeschrittenem Niveau ist es wichtig, reine Berechnungen streng von Operationen mit Seiteneffekten zu trennen. Dazu kann man das IO-Pattern (in funktionalen Sprachen) oder seine vereinfachten Analoga in C# mittels Func<T> und verzögerter Auswertung verwenden.
Praktische Technik: „Verzögerte Ausführung“
Anstatt eine Aktion mit Seiteneffekt sofort auszuführen, geben Sie einen Delegaten zurück, der sie ausführt:
public static Func<int> CreateFileReadOperation(string path){ return () => File.ReadAllText(path).Length; // Seiteneffekt verzögert}
// Verwendungvar operation = CreateFileReadOperation("data.txt");// ... reine Logik ...int length = operation(); // Seiteneffekt wird hier ausgeführt2. Funktionskomposition und Currying
Komposition ist die Kombination von zwei oder mehr Funktionen zur Erstellung einer neuen. Currying ist die Umwandlung einer Funktion mit mehreren Argumenten in eine Kette von Funktionen mit einem Argument. Diese Techniken machen Code modularer und wiederverwendbarer.
Komposition mit LINQ und eigenen Methoden
In C# kann Komposition durch Erweiterungsmethoden implementiert werden:
public static class FuncExtensions{ public static Func<T, TResult> Compose<T, TIntermediate, TResult>( this Func<T, TIntermediate> first, Func<TIntermediate, TResult> second) => x => second(first(x));}
// Beispiel: f(x) = x + 2, g(x) = x * 3, h = f ∘ gFunc<int, int> addTwo = x => x + 2;Func<int, int> multiplyByThree = x => x * 3;var composed = multiplyByThree.Compose(addTwo); // h(x) = (x*3) + 2Console.WriteLine(composed(5)); // 17Currying in C#
Currying ermöglicht die partielle Anwendung einer Funktion:
public static Func<int, Func<int, int>> CurriedAdd = x => y => x + y;
var addFive = CurriedAdd(5); // Partielle AnwendungConsole.WriteLine(addFive(10)); // 15
// Komplexeres Beispiel: Logging-Funktion mit Präfixpublic static Func<string, Func<string, string>> CreateLogger = prefix => message => $"[{prefix}] {message}";
var infoLogger = CreateLogger("INFO");Console.WriteLine(infoLogger("System gestartet")); // [INFO] System gestartet3. Monaden und Arbeiten mit Option (Maybe)
Eine Monade ist ein Pattern, das es erlaubt, Funktionen sequenziell auf Werte anzuwenden, die in einen Kontext eingewickelt sind (z. B. die Möglichkeit des Fehlens eines Wertes oder Asynchronität). In C# ist die Implementierung von Option<T> oder Maybe<T> beliebt, um null zu vermeiden.
Einfache Implementierung von Option
public class Option<T>{ private readonly T _value; private readonly bool _hasValue;
pri