JavaRush /Курсы /C# SELF /Динамические типы ( dynamic

Динамические типы ( dynamic) и DLR

C# SELF
63 уровень , 2 лекция
Открыта

1. Введение

C# — представитель строготипизированных языков. Это значит, что на этапе компиляции переменная должна иметь определённый тип, и обращаться к членам класса можно только в соответствии с этим типом. Это безопасно, удобно, понятно IDE и компилятору.

Но иногда требуется обращаться к объектам, структура которых может быть неизвестна на этапе компиляции — например, когда:

  • Исполняется код, написанный на другом языке .NET (например IronPython или IronRuby), где типизация динамическая.
  • При работе с COM-объектами (например, с Microsoft Office через Interop).
  • При взаимодействии с динамическими структурами данных: например, слабоформализованными JSON-объектами.
  • Не хочется писать кучу шаблонного кода, а проще “поверить” объекту на слово и попробовать вызвать у него нужный метод по имени.

С появлением в C# 4.0 ключевого слова dynamic и платформы DLR появилась возможность писать код, который ведёт себя “как в Python, только в C#”: проверка будет на этапе выполнения, а не во время компиляции. Через это волшебное слово можно заставить компилятор быть менее строгим и вручную проверить, есть ли нужный метод/свойство только когда программа реально дойдёт до этого места.

Что такое DLR?

Dynamic Language Runtime (DLR) — это инфраструктурное расширение к CLR (Common Language Runtime), которое позволяет реализовать поддержку динамических языков (и динамического поведения в статических языках). DLR берёт на себя выполнение операций, которые не могут быть проверены компилятором: например, вызов метода по имени, если тип станет известен только во время работы программы. Благодаря DLR, C# получает "динамический режим".

2. Ключевое слово dynamic: как оно работает

Отличие от object

Некоторые новички думают, что dynamic — это просто более модный способ написать object и каждый раз делать приведение. Это не так.

  • Переменная типа object всегда требует явного приведения к конкретному типу для вызова методов (и компилятор это проверяет).
  • Переменная типа dynamic даёт команду компилятору: “поверь мне, я сам знаю, какой там будет тип — разберёмся на месте!”. Если метода не существует — будет исключение во время выполнения.

Пример:


// Пример с object
object obj = "Привет, C#!";
// obj.ToUpper(); // Ошибка компиляции! Компилятор не знает, что obj — строка
string s = ((string)obj).ToUpper(); // Только так

// Пример с dynamic
dynamic dyn = "Привет, C#!";
string upper = dyn.ToUpper(); // Компилятор не возражает, проверка будет в рантайме

С dynamic можно обращаться к несуществующим свойствам и методам. Если их нет — будет исключение типа RuntimeBinderException.

Использование в примере приложения

Допустим, в нашем мини-приложении есть блок работы с JSON-данными, структура которых может меняться на лету. С dynamic можно обращаться к свойствам, будто они всегда существуют:


dynamic user = new System.Dynamic.ExpandoObject();
user.Name = "Вася";
user.Age = 35;
Console.WriteLine($"Имя: {user.Name}, возраст: {user.Age}");

3. DLR в действии: что происходит под капотом

Когда переменная объявляется как dynamic, компилятор C# запоминает, что любые обращения к методам, свойствам, индексаторам, операторам и т.д. над этим объектом он не может проверить заранее.

Вместо этого на месте любого динамического вызова вставляется код обращения к системному “свободному диспетчеру”. DLR наблюдает: а существует ли на самом деле тот метод или свойство, к которому мы пространно, небрежно и с оптимизмом обращаемся? Если да — выполняет вызов, если нет — кинет исключение.

Визуальная схема


+------------------------+
| Код с dynamic          |
+----------+-------------+
           |
           v
+----------+-------------+
| Компилятор C# вставляет|
| "запрос к DLR"         |
+----------+-------------+
           |
           v
+----------+-------------+
| В рантайме DLR ищет    |
| метод/свойство         |
+----------+-------------+
           |
      /---------\
      |         |
   найден   не найден
      |         |
      v         v
  вызов      исключение
   метода    (RuntimeBinderException)

DLR заботится о поиске методов и свойств в объекте — и даже кэширует часто используемые динамические вызовы!

Немного истории

DLR создавался для поддержки языков, которые изначально динамические: IronPython, IronRuby, а также для облегчения интеграции с динамическими объектами в C#. Сегодня это активно используется при работе с объектами типа ExpandoObject, DynamicObject, а также с COM и динамически сериализованными данными.

4. Сравнение: static vs dynamic

Давайте сравним, как выглядят вызовы свойств и методов в статической и динамической парадигме:

Способ Объявление Проверка времени компиляции Поведение при ошибке
object
object obj = ...
Требует каст к нужному типу Ошибка компиляции или InvalidCastException
dynamic
dynamic dyn = ...
Нет, всё в рантайме RuntimeBinderException, если метода нет
статический тип
MyClass a = ...
Проверяется компилятором Ошибка компиляции

Пример для наглядности:


// Статическая типизация
MyUser u = new MyUser();
u.PrintInfo(); // Проверяется компилятором

// dynamic
dynamic du = new MyUser();
du.PrintInfo(); // Компилятор не проверяет, найдёт метод только в рантайме

// object
object ou = new MyUser();
// ou.PrintInfo(); // Ошибка компиляции
((MyUser)ou).PrintInfo(); // Нужно явно привести к типу

Статические методы и dynamic


dynamic d = "Hello";
Console.WriteLine(d.Length); // 5 — всё хорошо

Console.WriteLine(d.FakeMethod()); // RuntimeBinderException во время исполнения

Но стоит быть осторожным! Если вы опечатались или имя метода не существует — это проявится только во время работы приложения. Классический пример "ошибки на продакшене”.

5. Практические примеры: динамика в реальных задачах

Работа с ExpandoObject


using System.Dynamic;

dynamic person = new ExpandoObject();
person.Name = "Сергей";
person.Greet = (Action)(() => Console.WriteLine($"Привет, {person.Name}!"));

person.Greet(); // Выведет: Привет, Сергей!

Здесь мы на лету добавили свойство Name и даже делегат Greet. Очень похоже на работу с объектами в JavaScript.

Привязка к структурам JSON


using System.Dynamic;
using Newtonsoft.Json;

string json = @"{""name"": ""Андрей"", ""age"": 30}";
dynamic obj = JsonConvert.DeserializeObject
  
   (json); Console.WriteLine(obj.name); // Андрей Console.WriteLine(obj.age); // 30 
  

Обратите внимание: если написать obj.surname — будет ошибка уже во время выполнения.

COM-Interop (Office Automation)


dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
excel.Visible = true; // “Видим” свойство, хотя компилятор не знает его типа

6. Полезные нюансы

DLR, dynamic и использование с другими .NET-языками

  • IronPython/IronRuby: можно инстанцировать объекты этих языков и вызывать их методы из C# через dynamic.
  • COM-объекты: автоматическая обёртка методов и свойств через dynamic.
  • Кастомные динамические объекты: реализуя интерфейс IDynamicMetaObjectProvider, можно сделать свой класс с динамическими вызовами.

Пример интеграции с 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

Как реализуются свои динамические типы

Обычно проще унаследоваться от DynamicObject.


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 (не выбрасывает исключение)

Когда dynamic — вредно

  • Снижает производительность (динамические вызовы медленнее статических).
  • Усложняет отладку и поддержку.
  • Лишает большинства плюшек IDE: автодополнения, рефакторинга, поиска по использованию.
  • Может привести к ошибкам, которые обнаруживаются только у пользователя или на сервере.

Когда стоит реально использовать dynamic

  • Интеграция с внешними библиотеками, где невозможно или нецелесообразно писать обёртки (COM, Python, JavaScript).
  • Манипулирование слабо типизированными структурами (JSON/XML/ExpandoObject).
  • Для написания фреймворков, метапрограммирования и гибких API, которые должны быть динамическими по замыслу.

Во всех других случаях лучше использовать статические типы и радоваться помощи компилятора :-)

7. Типичные ошибки при работе с dynamic и DLR

Ошибка №1: Обращение к несуществующим членам.
Вызов несуществующего метода или свойства для dynamic приводит к RuntimeBinderException в рантайме, что сложно отловить на этапе разработки.

Ошибка №2: Игнорирование проверки типов.
Без проверки типов перед вызовом (например, if (obj is IDictionary<string, object>)) можно получить неожиданные ошибки в рантайме.

Ошибка №3: Использование dynamic в публичных API.
Динамические типы усложняют поддержку кода, так как IDE и анализаторы не могут подсказать правильные члены, что снижает читаемость.

Ошибка №4: Злоупотребление dynamic в производительном коде.
Динамические вызовы медленнее статических и увеличивают накладные расходы. Используйте dynamic только при необходимости.

2
Задача
C# SELF, 63 уровень, 2 лекция
Недоступна
Простое использование dynamic
Простое использование dynamic
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ