CodeGym /Kurse /C# SELF /Verwendung von Reflection

Verwendung von Reflection

C# SELF
Level 63 , Lektion 1
Verfügbar

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
Type
Der Haupteinstieg — alles beginnt hier
FieldInfo
Informationen über ein Feld
PropertyInfo
Informationen über eine Property
MethodInfo
Informationen über eine Methode
ConstructorInfo
Informationen über einen Konstruktor
EventInfo
Informationen über ein Event
MemberInfo
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
GetFields(BindingFlags.Public | ...)
Alles, was andere sehen können
Private Felder
GetFields(BindingFlags.NonPublic | ...)
Versteckte interne
Nur Instanz
BindingFlags.Instance
Nur nicht-statische
Nur static
BindingFlags.Static
Nur statische
Alles zusammen
BindingFlags.Public | NonPublic | Instance | Static
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)
GetField, GetFields
FieldInfo
.GetValue(obj), .SetValue(obj, x)
Property (prop)
GetProperty, GetProperties
PropertyInfo
.GetValue(obj), .SetValue(obj, x)
Methode (method)
GetMethod, GetMethods
MethodInfo
.Invoke(obj, parameter)
Konstruktor
GetConstructor, GetConstructors
ConstructorInfo
.Invoke(parameter)

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.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION