Metaprogrammierung in C#: Von Reflection bis Codegenerierung

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Metaprogrammierung in C#: Wenn Code Code schreibt



Stellen Sie sich vor, Sie sind ein Architekt, der nicht nur ein Haus baut, sondern einen Roboter-Bauarbeiter, der selbstständig Wände hochzieht, Leitungen verlegt und Fassaden streicht. In der Welt von C# ist dieser „Roboter“ die Metaprogrammierung – die Kunst, Programme zu schreiben, die andere Programme (oder sich selbst) zur Laufzeit erstellen, analysieren oder modifizieren.



Wozu braucht man das? Stellen Sie sich vor, Sie schreiben Dutzende gleichartiger Klassen für die Arbeit mit JSON, erstellen komplexe Plugin-Systeme oder haben keine Lust, hunderte Methoden manuell per Reflection aufzurufen. Metaprogrammierung in C# ermöglicht es, Routineaufgaben zu automatisieren, flexible Frameworks zu erstellen und die Entwicklung um ein Vielfaches zu beschleunigen. Heute befassen wir uns mit den drei Säulen dieser Technik: Reflection, Expression Trees und dynamischer Codegenerierung.



Dieser Artikel richtet sich an alle, die bereits sicher in C# programmieren, aber einen Blick unter die Haube der CLR werfen und die Magie erlernen möchten, die einen Senior von einem Mid-Level-Entwickler unterscheidet. Los geht's!



Reflection: Röntgenblick für Ihren Code



Reflection ist das Fundament der Metaprogrammierung in C#. Sie ermöglicht es, Informationen über Typen, Methoden, Eigenschaften und Felder zur Laufzeit zu erhalten und diese dynamisch aufzurufen. Stellen Sie sich vor, Sie betrachten ein Objekt wie ein offenes Buch und können jede Seite darin lesen.



Informationen über einen Typ abrufen



Der einfachste Weg ist die Verwendung des Schlüsselworts typeof oder der Methode GetType():



Type stringType = typeof(string);Console.WriteLine($"Name: {stringType.Name}"); // StringConsole.WriteLine($"Namespace: {stringType.Namespace}"); // SystemConsole.WriteLine("Ist es eine Klasse? " + stringType.IsClass); // True


Dynamischer Methodenaufruf



Angenommen, Sie haben eine Klasse Calculator und möchten deren Methode Add aufrufen, deren Name erst zur Laufzeit bekannt ist (z. B. aus einer Konfiguration):



public class Calculator{    public int Add(int a, int b) => a + b;}

// Reflection verwendenCalculator calc = new Calculator();Type calcType = typeof(Calculator);MethodInfo? addMethod = calcType.GetMethod("Add");

if (addMethod != null){ int result = (int)addMethod.Invoke(calc, new object[] { 5, 3 }); Console.WriteLine($"Ergebnis: {result}"); // 8}


Wann ist Reflection nützlich?

  • Serialisierung / Deserialisierung – Bibliotheken wie Newtonsoft.Json nutzen Reflection intensiv.
  • Plugins – Laden externer Assemblys und Aufruf ihrer Methoden.
  • Testen – Zugriff auf private Felder und Methoden (auch wenn man es besser vermeiden sollte).


Wichtig! Reflection ist eine langsame Operation. Wenn Sie dieselbe Methode tausende Male aufrufen, denken Sie über das Caching von MethodInfo oder die Verwendung von Delegaten nach.



Expression Trees: Code als Daten



Expression Trees sind eine Möglichkeit, Code als Datenstruktur (einen Baum) darzustellen. Sie können diesen Code analysieren, modifizieren und sogar „on the fly“ kompilieren. Dies ist ein mächtiges Werkzeug zur Erstellung von ORMs (Entity Framework), Validatoren und dynamischen Abfragen.



Erstellen eines einfachen Expression Trees



Anstatt einen Lambda-Ausdruck x => x * 2 zu schreiben, bauen wir ihn manuell auf:



using System.Linq.Expressions;

// Parameter x erstellenParameterExpression param = Expression.Parameter(typeof(int), "x");// Konstante 2 erstellenConstantExpression constant = Expression.Constant(2);// Multiplikation x * 2 erstellenBinaryExpression body = Expression.Multiply(param, constant);// Alles zu einem Lambda zusammenbauen: x => x * 2Expression<Func<int, int>> lambda = Expression.Lambda<Func<int, int>>(body, param);

// Kompilieren und ausführenFunc<int, int> compiled

Blogs

Buchempfehlungen