Что такое ключевое слово dynamic в C#?

Ответ

dynamic — это тип, который включает проверку типов не во время компиляции, а во время выполнения (runtime). Объявленные как dynamic сущности пропускают статическую проверку типов компилятором C#. Это мощный инструмент, который следует использовать осознанно.

Основные сценарии использования:

  1. Работа с COM-объектами (например, Office Interop): Упрощает API, который часто возвращает object.
  2. Использование динамических языков через DLR (Dynamic Language Runtime): Взаимодействие с кодом на IronPython или IronRuby.
  3. Работа с JSON/XML данными неизвестной структуры (хотя сейчас предпочтительнее System.Text.Json или Newtonsoft.Json).
  4. Создание динамических объектов с помощью ExpandoObject.

Пример:

dynamic dynamicVariable = "Это строка";
Console.WriteLine(dynamicVariable.Length); // Работает, у string есть Length
Console.WriteLine(dynamicVariable.ToUpper()); // Работает

dynamicVariable = 42;
Console.WriteLine(dynamicVariable + 10); // Работает, сложение чисел
// Console.WriteLine(dynamicVariable.ToUpper()); // Выбросит RuntimeBinderException!

Важные особенности и предостережения:

  • Производительность: Операции с dynamic медленнее из-за разрешения вызовов в рантайме.
  • Безопасность типов: Ошибки, которые компилятор поймал бы статически (например, вызов несуществующего метода), проявятся только во время выполнения как Microsoft.CSharp.RuntimeBinder.RuntimeBinderException.
  • IntelliSense: Подсказки по членам объекта в IDE отсутствуют.
  • Преобразования: Компилятор неявно преобразует dynamic в любой другой тип.
    dynamic d = 5;
    int i = d; // OK, преобразование dynamic -> int в runtime
    string s = d; // RuntimeBinderException, если значение не строковое

Правило: Используйте dynamic только когда выгода от упрощения кода перевешивает потерю безопасности типов и производительности, и когда статически типизированная альтернатива (интерфейсы, обобщенные методы) неприменима.

Ответ 18+ 🔞

Слушай, вот тебе про dynamic по-человечески, без этих заумных мантр.

Представь, что компилятор C# — это такой занудный охранник на проходной. Ты ему показываешь пропуск (тип переменной), он смотрит, сверяет с базой (системой типов) и говорит: «Да, проходи, у тебя тут метод ToUpper() в правах указан, всё чисто».

А dynamic — это волшебная ксива, которая этому охраннику говорит: «Отстань, братан, я сам разберусь, что и как можно делать с этой штукой. Пропусти и не мешайся». Охранник пожимает плечами и пускает. А разбираться-то с твоей переменной придётся уже во время работы программы, когда все уже разошлись по кабинетам.

Где это может пригодиться, не сходя с ума:

  1. Когда лезешь в старый COM-объект, например, в Excel. Там вечно возвращается object, и чтобы с ним работать, нужно танцевать с приведением типов и рефлексией. dynamic позволяет просто написать excel.Cells[1, 1].Value = "Привет" и не ебаться.
  2. Когда принимаешь JSON, а какой там черт ногу сломит. Раньше, до нормальных сериализаторов, это было спасением. Сейчас, конечно, System.Text.Json рулит, но для каких-нибудь сверхдинамичных сценариев ещё актуально.
  3. Когда хочешь сделать объект, у которого свойства появляются на лету. Типа ExpandoObject. Создал, накидал полей как бог на душу положит — и работает.

Смотри, как это выглядит в жизни:

dynamic штука = "строка";
Console.WriteLine(штука.Length); // Выведет 7, всё ок
Console.WriteLine(штука.ToUpper()); // Выведет "СТРОКА", тоже ок

штука = 10; // А теперь переприсвоили на число
Console.WriteLine(штука + 5); // 15, сложение работает
// Console.WriteLine(штука.ToUpper()); // А вот это — ПИЗДЕЦ! RuntimeBinderException настигнет тебя в рантайме.

Теперь про подводные ебеня:

  • Скорость. Всё это разрешение методов и свойств «на ходу» — не бесплатное. Оно медленнее, чем прямой вызов. Если в тугом цикле — забудь.
  • Безопасность. Охранника-то ты обманул. Если попробуешь вызвать метод, которого нет, компилятор молча проглотит это, а программа хряснется во время выполнения с RuntimeBinderException. Все ошибки, которые могли бы быть пойманы на этапе написания, вылезут только когда пользователь уже будет тыкать в интерфейс.
  • Подсказки (IntelliSense). Их не будет. IDE просто не знает, что там у тебя в dynamic плавает. Придется помнить всё самому или лезть в документацию.
  • Преобразования. Компилятор на них забивает. Сможешь ли ты dynamic d = 5 запихнуть в int — решится в момент выполнения.

Итог, блядь: dynamic — это как мощная болгарка. В нужных руках и для конкретной задачи (демонтаж старого COM-забора) — незаменима. Но если начать пилить ей всё подряд в повседневном коде, то можно и пальцы отхватить, и проект в опилки превратить. Используй только когда реально нет нормального статически типизированного пути, и ты готов платить безопасностью и скоростью за это удобство.