1. Einführung
In C# ist jede Klasse (oder struct) eine Menge von Membern. Dazu gehören:
- Felder (fields): Variablen, die Daten speichern.
- Eigenschaften (properties): “smarte” Wrapper mit get/set.
- Methoden (methods): Funktionalität.
- Konstruktoren (constructors): Wege, eine Instanz zu erstellen.
- Ereignisse (events): Publish-Subscribe auf clevere Weise.
Stell dir vor, du hättest eine magische Lupe, mit der du in jeden Typ hineinschauen kannst, seinen Inhalt kennst und ihn sogar änderst. Genau das ist Reflection.
Namespace und die wichtigsten Reflection-Typen
Bevor du loslegst, vergiss nicht, den Namespace einzubinden:
using System.Reflection;
Wichtige Klassen und Interfaces:
| Klasse/Interface | Beschreibung |
|---|---|
|
Der Haupteinstieg — alles beginnt hier |
|
Informationen über ein Feld |
|
Informationen über eine Property |
|
Informationen über eine Methode |
|
Informationen über einen Konstruktor |
|
Informationen über ein Event |
|
Allgemeiner “Elternteil” für alle oben |
2. Mitglieder eines Typs über Reflection bekommen
Felder und Properties bekommen
Beispiel: Felder und Properties auflisten
Angenommen, wir haben diese Klasse:
public class Person
{
public string Name { get; set; }
public int Age;
private string Secret = "Sekretnoye slovo";
}
Felder bekommen:
Type personType = typeof(Person);
FieldInfo[] fields = personType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fields)
{
Console.WriteLine($"Feld: {field.Name}, Typ: {field.FieldType.Name}, Zugriff: {field.Attributes}");
}
Properties bekommen:
PropertyInfo[] properties = personType.GetProperties(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties)
{
Console.WriteLine($"Property: {property.Name}, Typ: {property.PropertyType.Name}");
}
Erklärungen
- BindingFlags — ein Set von “Flags”, die sagen, welche Member gesucht werden sollen (public/private, instance/static usw.).
- Felder und Properties unterscheiden sich: Feld — ein Datenstück, Property — die get/set-Methoden, die das Feld verbergen.
Methoden bekommen
MethodInfo[] methods = personType.GetMethods(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var method in methods)
{
Console.WriteLine($"Methode: {method.Name}");
}
Du wirst überrascht sein, wie viele Methoden ausgegeben werden. Dort sind Getter/Setter der Properties und Hilfsmethoden aus object dabei!
Nützlicher Filter: um nur deine “expliziten” Methoden zu bekommen, kannst du nach Namen oder Attributen filtern.
Welche BindingFlags wann
| Was gesucht wird | Beispiel Aufruf | Beschreibung |
|---|---|---|
| Öffentliche Felder | |
Alles, was andere sehen können |
| Private Felder | |
Versteckte interne |
| Nur Instanz | |
Nur nicht-statische |
| Nur static | |
Nur statische |
| Alles zusammen | |
Vollständige Liste |
3. Dynamisches Erstellen einer Instanz eines Typs
Warum ist das cool?
Du kannst ein Objekt erstellen, ohne seinen genauen Typ zur Compile-Zeit zu kennen.
Einfacher Weg — über Activator.CreateInstance
// Wir bekommen Type auf irgendeine Weise, z.B.:
Type myType = typeof(Person);
// Objekt erstellen:
object obj = Activator.CreateInstance(myType);
// Jetzt ist obj eine Instanz von Person, aber als object
Wenn die Klasse keinen parameterlosen Konstruktor hat, kannst du Parameter übergeben:
public class Animal
{
public string Name { get; }
public Animal(string name) { Name = name; }
}
// ...
Type animalType = typeof(Animal);
object dog = Activator.CreateInstance(animalType, "Sharik");
Wichtig!
Console.WriteLine(dog.GetType().Name); // Animal
Mehr kontrollierter Weg — über ConstructorInfo
ConstructorInfo ctor = animalType.GetConstructor(new Type[] { typeof(string) });
object dog = ctor.Invoke(new object[] { "Sharik" });
Im Alltag benutzt man meistens Activator, aber manchmal braucht man genaue Parameter oder arbeitet mit privaten Konstruktoren — da hilft ConstructorInfo.
4. Zugriff auf Felder und Properties per Name
Feld lesen und ändern
Person p = new Person();
Type t = p.GetType();
FieldInfo field = t.GetField("Age");
field.SetValue(p, 42);
Console.WriteLine(field.GetValue(p)); // 42
Arbeiten mit privaten Feldern:
FieldInfo secret = t.GetField("Secret", BindingFlags.NonPublic | BindingFlags.Instance);
if (secret != null)
{
secret.SetValue(p, "Oj, sekret raskryt");
Console.WriteLine(secret.GetValue(p));
}
else
{
Console.WriteLine("Feld Secret nicht gefunden");
}
Vorsicht! Missbrauche keinen Zugriff auf private Felder — das bricht Kapselung und kann zu Bugs führen.
Property lesen und ändern
PropertyInfo pi = t.GetProperty("Name");
pi.SetValue(p, "Vasya");
Console.WriteLine(pi.GetValue(p)); // Vasya
Properties sind keine Felder, sondern Methoden get/set hinter den Kulissen.
Warum klappt es manchmal nicht?
Wenn dein Feld oder deine Property privat ist, benutze unbedingt die passenden BindingFlags (z.B. NonPublic). Wenn eine Property keinen Setter hat, kannst du per Reflection nichts hineinschreiben — diese Regel bleibt bestehen.
5. Methoden per Name aufrufen
Aufruf einer einfachen Methode ohne Parameter
public class Greeter
{
public void SayHello()
{
Console.WriteLine("Privet!");
}
}
Greeter g = new Greeter();
Type t = g.GetType();
MethodInfo mi = t.GetMethod("SayHello");
mi.Invoke(g, null); // Privet!
Aufruf einer Methode mit Parametern
public class MathOp
{
public int Add(int x, int y) => x + y;
}
MathOp calc = new MathOp();
Type t = calc.GetType();
MethodInfo mi = t.GetMethod("Add");
object result = mi.Invoke(calc, new object[] { 7, 5 });
Console.WriteLine(result); // 12
Wenn die Methode static ist, übergib im ersten Argument von Invoke null.
Arbeiten mit überladenen Methoden
MethodInfo mi = t.GetMethod(
"Add",
new Type[] { typeof(int), typeof(int) }
);
Oder durchlaufe und filtere manuell:
foreach(var method in t.GetMethods()) {
if (method.Name == "Add" && method.GetParameters().Length == 2)
{
// ... unsere Methode!
}
}
6. Nützliche Feinheiten
Wichtige Member, Methoden und Properties zur Analyse eines Typs
| Klassenmember | Methode zum Holen von Info | Ergebnis-Klasse | Wie lesen/ändern |
|---|---|---|---|
| Feld (field) | |
|
|
| Property (prop) | |
|
|
| Methode (method) | |
|
|
| Konstruktor | |
|
|
Wozu das im echten Leben?
- In ORM-Frameworks (z.B. Entity Framework baut SQL-Queries aus deinen Models).
- In Serialisierungs- und Parsing-Systemen (z.B. JSON-Serializer).
- In universellen Bibliotheken für Logging, Testing, UI-Generatoren.
- Bei Bewerbungsgesprächen — gerne wird gefragt: “Wie würdest du einen universellen Object-Copier schreiben?”.
- Zum Schreiben von Plugins, wenn dein Programm Code lädt und aufruft, der von anderen geschrieben wurde.
Auch wenn es jetzt wie Hacker-Magie aussieht — glaub mir, es wird nützlich sein!
7. Typische Fehler und Besonderheiten
Entwickler verwechseln oft BindingFlags — sehen private Felder nicht oder bekommen zu viele Methoden. Überprüfe immer die Kombination der Flags: für private Felder nutze NonPublic, für statische — Static usw.
Beim Aufruf von Methoden via Invoke übergib die Argumente in derselben Reihenfolge und Typen wie in der Deklaration. Falsche Reihenfolge/Typ führt zu TargetParameterCountException oder ArgumentException. Das Ergebnis wird als object zurückgegeben — vergiss nicht zu casten (oder pattern matching zu nutzen).
Denk an Performance: Reflection ist langsamer als direkte Aufrufe. Cache in heißen Pfaden gefundene Type/MethodInfo/PropertyInfo oder generiere vorher Delegates.
Du wirst oft Hilfsmethoden wie get_PropertyName und set_PropertyName sehen. Das sind Accessor-Methoden für Properties — du musst sie nicht manuell erstellen.
Wenn du private Felder oder Properties änderst, brichst du Kapselung. Falscher Gebrauch kann schwer zu findende Bugs bringen. Wie Onkel Ben sagte: “Mit großer Kraft kommt große Verantwortung” — benutze es mit Vorsicht, besonders in Bibliotheksprojekten.
GO TO FULL VERSION