CodeGym /Kurse /C# SELF /Dynamische Typen ( dynamic

Dynamische Typen ( dynamic) und DLR

C# SELF
Level 63 , Lektion 2
Verfügbar

1. Einführung

C# ist eine Vertreterin streng typisierter Sprachen. Das heißt, dass eine Variable zur Kompilierzeit einen definierten Typ haben muss und auf Mitglieder einer Klasse nur entsprechend diesem Typ zugegriffen werden darf. Das ist sicher, praktisch und verständlich für IDE und Compiler.

Aber manchmal muss man auf Objekte zugreifen, deren Struktur zur Kompilierzeit unbekannt sein kann — zum Beispiel, wenn:

  • Code ausgeführt wird, der in einer anderen .NET-Sprache geschrieben ist (z. B. IronPython oder IronRuby), wo die Typisierung dynamisch ist.
  • Mit COM-Objekten gearbeitet wird (zum Beispiel mit Microsoft Office über Interop).
  • Mit dynamischen Datenstrukturen interagiert wird: zum Beispiel mit schwach formalisierten JSON-Objekten.
  • Man nicht viel Boilerplate-Code schreiben möchte, sondern dem Objekt einfach „glauben“ und versuchen will, die benötigte Methode per Name aufzurufen.

Mit dem Einzug des Schlüsselworts dynamic in C# 4.0 und der DLR-Plattform wurde es möglich, Code zu schreiben, der sich „wie in Python, aber in C#“ verhält: die Prüfung erfolgt zur Laufzeit, nicht beim Kompilieren. Mit diesem magischen Wort kann man den Compiler weniger streng machen und erst zur Laufzeit prüfen, ob die benötigte Methode/Property existiert, wenn das Programm wirklich an diese Stelle gelangt.

Was ist DLR?

Dynamic Language Runtime (DLR) ist eine infrastrukturelle Erweiterung der CLR (Common Language Runtime), die die Unterstützung dynamischer Sprachen (und dynamischen Verhaltens in statischen Sprachen) ermöglicht. DLR übernimmt die Ausführung von Operationen, die der Compiler nicht vorab prüfen kann: zum Beispiel den Methodenaufruf per Namen, wenn der Typ erst zur Laufzeit bekannt wird. Dank DLR bekommt C# einen „dynamischen Modus“.

2. Das Schlüsselwort dynamic: wie es funktioniert

Unterschied zu object

Manche Anfänger denken, dass dynamic nur eine schickere Art ist, object zu schreiben und jedes Mal zu casten. Das ist nicht so.

  • Eine Variable vom Typ object erfordert immer ein explizites Casting zu einem konkreten Typ, um Methoden aufzurufen (und der Compiler prüft das).
  • Eine Variable vom Typ dynamic gibt dem Compiler die Anweisung: „Vertrau mir, ich weiß selbst, welcher Typ dort sein wird — wir klären das zur Laufzeit!“. Wenn die Methode nicht existiert, gibt es eine Ausnahme zur Laufzeit.

Beispiel:


// Beispiel mit object
object obj = "Hello, C#!";
// obj.ToUpper(); // Kompilierungsfehler! Der Compiler weiß nicht, dass obj ein string ist
string s = ((string)obj).ToUpper(); // Nur so

// Beispiel mit dynamic
dynamic dyn = "Hello, C#!";
string upper = dyn.ToUpper(); // Der Compiler protestiert nicht, die Prüfung erfolgt zur Laufzeit

Mit dynamic kann man auf nicht vorhandene Properties und Methoden zugreifen. Wenn sie fehlen, wird eine Ausnahme vom Typ RuntimeBinderException geworfen.

Verwendung im Beispielprogramm

Angenommen, in unserer Mini-Anwendung gibt es einen Block, der mit JSON-Daten arbeitet, deren Struktur sich zur Laufzeit ändern kann. Mit dynamic kann man auf Properties zugreifen, als ob sie immer existieren:


dynamic user = new System.Dynamic.ExpandoObject();
user.Name = "Vasya";
user.Age = 35;
Console.WriteLine($"Name: {user.Name}, age: {user.Age}");

3. DLR in Aktion: was unter der Haube passiert

Wenn eine Variable als dynamic deklariert wird, merkt sich der C#-Compiler, dass er Zugriffe auf Methoden, Properties, Indexer, Operatoren usw. für dieses Objekt nicht vorab prüfen kann.

Stattdessen fügt er an jede dynamische Stelle Code ein, der einen Aufruf an einen systemweiten „freien Dispatcher“ macht. DLR prüft: existiert diese Methode oder dieses Property, auf das wir so frei, lässig und optimistisch zugreifen wollen, tatsächlich? Wenn ja — führt es den Aufruf aus, wenn nein — wird eine Ausnahme geworfen.

Visuelles Schema


+------------------------+
| Code mit dynamic       |
+----------+-------------+
           |
           v
+----------+-------------+
| C#-Compiler fügt ein   |
| "Anfrage an DLR"       |
+----------+-------------+
           |
           v
+----------+-------------+
| Zur Laufzeit sucht DLR |
| Methode/Property       |
+----------+-------------+
           |
      /---------\
      |         |
   gefunden   nicht gefunden
      |         |
      v         v
  Aufruf      Ausnahme
  der Methode (RuntimeBinderException)

DLR kümmert sich um die Suche nach Methoden und Properties im Objekt — und cached sogar häufig verwendete dynamische Aufrufe!

Ein bisschen Geschichte

DLR wurde entwickelt, um Sprachen zu unterstützen, die von Haus aus dynamisch sind: IronPython, IronRuby, und um die Integration mit dynamischen Objekten in C# zu erleichtern. Heute wird es aktiv bei der Arbeit mit Objekten wie ExpandoObject, DynamicObject, sowie mit COM und dynamisch serialisierten Daten verwendet.

4. Vergleich: static vs dynamic

Vergleichen wir, wie Aufrufe von Properties und Methoden in der statischen und der dynamischen Paradigmen aussehen:

Methode Deklaration Kompilierungszeit-Prüfung Verhalten bei Fehler
object
object obj = ...
Erfordert Cast zum benötigten Typ Kompilierungsfehler oder InvalidCastException
dynamic
dynamic dyn = ...
Nein, alles zur Laufzeit RuntimeBinderException, falls Methode fehlt
statischer Typ
MyClass a = ...
Wird vom Compiler geprüft Kompilierungsfehler

Beispiel zur Veranschaulichung:


// Statische Typisierung
MyUser u = new MyUser();
u.PrintInfo(); // Wird vom Compiler geprüft

// dynamic
dynamic du = new MyUser();
du.PrintInfo(); // Compiler prüft nicht, Methode wird erst zur Laufzeit gesucht

// object
object ou = new MyUser();
// ou.PrintInfo(); // Kompilierungsfehler
((MyUser)ou).PrintInfo(); // Muss explizit gecastet werden

Statische Methoden und dynamic


dynamic d = "Hello";
Console.WriteLine(d.Length); // 5 — alles gut

Console.WriteLine(d.FakeMethod()); // RuntimeBinderException zur Laufzeit

Aber Vorsicht! Wenn du dich vertippst oder ein Methodenname nicht existiert — das zeigt sich erst zur Laufzeit. Klassisches Beispiel für „Fehler in Production“.

5. Praktische Beispiele: Dynamik in realen Aufgaben

Arbeiten mit ExpandoObject


using System.Dynamic;

dynamic person = new ExpandoObject();
person.Name = "Steve";
person.Greet = (Action)(() => Console.WriteLine($"Hello, {person.Name}!"));

person.Greet(); // Gibt aus: Hello, Steve!

Hier haben wir zur Laufzeit das Property Name und sogar den Delegate Greet hinzugefügt. Sehr ähnlich zur Arbeit mit Objekten in JavaScript.

Binden an JSON-Strukturen


using System.Dynamic;
using Newtonsoft.Json;

string json = @"{""name"": ""Andrey"", ""age"": 30}";
dynamic obj = JsonConvert.DeserializeObject
  
   (json); Console.WriteLine(obj.name); // Andrey Console.WriteLine(obj.age); // 30 
  

Beachte: wenn du obj.surname schreibst — gibt es einen Fehler erst zur Laufzeit.

COM-Interop (Office Automation)


dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
excel.Visible = true; // Wir "sehen" das Property, obwohl der Compiler den Typ nicht kennt

6. Nützliche Feinheiten

DLR, dynamic und Nutzung mit anderen .NET-Sprachen

  • IronPython/IronRuby: Man kann Objekte dieser Sprachen instanziieren und ihre Methoden aus C# über dynamic aufrufen.
  • COM-Objekte: automatische Wrapper für Methoden und Properties über dynamic.
  • Eigene dynamische Objekte: Wenn man das Interface IDynamicMetaObjectProvider implementiert, kann man seine Klasse für dynamische Aufrufe fit machen.

Beispiel Integration mit IronPython


using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

ScriptEngine engine = Python.CreateEngine();
dynamic py = engine.Execute("class Test: pass\nTest()");
py.x = 42;
Console.WriteLine(py.x); // 42

Wie man eigene dynamische Typen implementiert

Normalerweise ist es einfacher, von DynamicObject zu erben.


using System.Dynamic;

class MyDynamic : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (binder.Name == "Ping")
        {
            result = "Pong!";
            return true;
        }
        result = null;
        return false;
    }
}

dynamic d = new MyDynamic();
Console.WriteLine(d.Ping); // Pong!
Console.WriteLine(d.Miss); // null (wirft keine Ausnahme)

Wann dynamic schadet

  • Vermindert die Performance (dynamische Aufrufe sind langsamer als statische).
  • Erhöht die Komplexität beim Debuggen und Warten des Codes.
  • Raubt den meisten IDE-Vorteilen: Autocomplete, Refactoring, Usage-Search.
  • Kann zu Fehlern führen, die erst beim Nutzer oder auf dem Server sichtbar werden.

Wann man dynamic wirklich nutzen sollte

  • Integration mit externen Bibliotheken, bei denen es unmöglich oder unpraktisch ist, Wrapper zu schreiben (COM, Python, JavaScript).
  • Manipulation schwach getypteter Strukturen (JSON/XML/ExpandoObject).
  • Zum Schreiben von Frameworks, Metaprogrammierung und flexiblen APIs, die von Natur aus dynamisch sein sollen.

In allen anderen Fällen ist es besser, statische Typen zu verwenden und sich über die Hilfe des Compilers zu freuen :-)

7. Typische Fehler bei der Arbeit mit dynamic und DLR

Fehler Nr.1: Zugriff auf nicht existierende Mitglieder.
Der Aufruf einer nicht existierenden Methode oder Property bei dynamic führt zur RuntimeBinderException zur Laufzeit, was während der Entwicklung schwer zu fangen ist.

Fehler Nr.2: Ignorieren von Typprüfungen.
Ohne Typchecks vor dem Aufruf (z. B. if (obj is IDictionary<string, object>)) kann man unerwartete Laufzeitfehler bekommen.

Fehler Nr.3: Verwendung von dynamic in öffentlichen APIs.
Dynamische Typen erschweren die Wartung des Codes, weil IDEs und Analyzer die richtigen Mitglieder nicht vorschlagen können, was die Lesbarkeit mindert.

Fehler Nr.4: Missbrauch von dynamic in Performance-kritischem Code.
Dynamische Aufrufe sind langsamer als statische und erhöhen den Overhead. Nutze dynamic nur wenn nötig.

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