Andriy Shyrokoryadov

.Net developer, data scientist

Разница между ключевыми словами var и dynamic в C# - вопрос №31 на собеседование C# / .NET

Текст к видео "Разница между ключевыми словами var и dynamic в C#" на канале YouTube

В процессе программирования на языке C# можно встретить синтаксис с ключевыми словами var и dynamic. На первый взгляд синтаксис с этим ключевыми словами может нести одинаковую смысловую нагрузку – мы не указываем тип переменной или объекта непосредственно, он будет определен когда-то позже. Однако это не совсем так. Разница между этими двумя ключевыми словами существенная и на этой почве в процессе собеседования могут возникнуть некоторые вопросы. Как правило эти вопросы касаются различий между этими двумя ключевыми словами.

Для начала давайте проведем эксперимент. Ниже представлен код, который выводит на экран абсолютно идентичный результат для трёх строчек вывода:

var a1 = 1;
object a2 = 1;
dynamic a3 = 1;
Console.WriteLine(a1.GetType()); // System.Int32
Console.WriteLine(a2.GetType()); // System.Int32
Console.WriteLine(a3.GetType()); // System.Int32

Первое отличие между ключевыми словами var и dynamic заключается в том, когда определяется тип переменной. В случае ключевого слова var это будет первое присваивание значения переменной, обозначенной ключевым словом var. После этого присваивания изменить тип значения переменной нельзя. Однако в случае ключевого слова dynamic, тип переменной можно изменять после первого присваивания.

Ключевое слово var:

var a = 1.0;
// следующая строчка вызовет ошибку компиляции
a = one;

Ключевое слово dynamic:

dynamic a = 1.0;
// следующая строчка будет нормально воспринята компилятором
a = one;

Из этого можно сделать вывод – определение типа переменной, обозначенной ключевым словом var, происходит на этапе компиляции, а фактический тип переменной обозначенной ключевым словом dynamic определяется в процессе выполнения приложения.

Вторым отличием является то, что во время создания кода на переменных типа dynamic можно вызывать различные методы и свойства. Такой код будет скомпилирован без проблем, даже если мы будем знать, что фактический тип переменной в типе dynamic не поддерживает данный метод или свойство.

dynamic a = 1.0;
a.someUnknownMethod(); // компиляция без ошибок

Здесь проблемы появятся после того, как мы запустим приложении с этим кодом – появится исключение типа RuntimeBinderException, потому что тип double(фактический тип переменной a) не поддерживает метод, который называется someUnknownMethod. Об исключениях и их обработке на канале снято отдельное видео – если данная тематика вам интересна, ссылка будет в правом верхнем углу.

Третье отличие это область видимости переменных, обозначенных ключевыми словами var и dynamic. Переменная, обозначенная ключевым словом var, это локальная переменная. Нельзя аргументы методов и конструкторов обозначать типом var, а также нельзя возвращать переменную типа var из метода. Также проблематично будет задекларировать поле или свойства класса с типом var. Однако всё это не относится к декларируемому типу dynamic. Поля и свойства классов, аргументы и возвращаемые значения, всё это может быть обозначено типом dynamic.

Переменные, обозначенные ключевым словом dynamic дают некоторую гибкость в написании кода, однако стоит помнить, что данный тип имеет также и ограничения:

  • переменные dynamic не работают с выражениями лямда;
  • переменные dynamic не работают с методами расширения, а из этого следует, что данные переменные не могут работать с выражениями LINQ, которые по своей сути являются методами расширения;
  • для переменной dynamic не работает IntelliSense в VisualStudio, то есть не будет подсказок среды разработки;

Просто так использовать ключевое слово dynamic не имеет смысла. Однако есть несколько сценариев, где ключевое слово dynamic может пригодиться:

  • если ваше приложение использует рефлексию, то объекты и переменные типа dynamic предоставят возможность писать более чистый и понятный код;
  • если ваше приложение интегрируется со старым кодом, в частности с библиотеками COM;
  • в фреймворке Asp.Net MVC есть специальная переменная ViewBag которая служит для передачи данных от контроллера в представление, данная переменная имеет тип dynamic;

Рассмотрим пример упрощения кода рефлексии. Если Вы еще не знакомы с рефлексией, я рекомендую вам посмотреть видео на тему рефлексии, которое доступно на моём канале – ссылка будет в правом верхнем углу. Предположим, что у нас есть файл библиотеки «Calculator», в которой определен класс “Calc” с методом “Sum”, который принимает 2 аргумента типа int.

Пример кода рефлексии без ключевого слова dynamic:

var asm = Assebly.Load(Calculator);
var calc = asm.GetType(Calculator.Calc);
object obj = Activator.CreateInstance(calc);
MethodInfo sumMethod = math.GetMethod(Sum);
object[] args = {2,3};
var result = sumMethod.Invoke(obj, args); // result будет равно 5.

Пример кода рефлексии с ключевым словом dynamic:

var asm = Assebly.Load(Calculator);
var calc = asm.GetType(Calculator.Calc);
dynamic obj = Activator.CreateInstance(calc);
var result = obj.Sum(2,3); // result будет равно 5.

Также я хотел бы сказать несколько слово на тему переменной ViewBag в фреймворке Asp.Net MVC. Если вы проходите собеседованию на позицию, где будет использоваться данный фреймворк, то вы смело можете ожидать вопрос какая разница между переменными ViewBag и ViewData. По сути, обе эти переменные выполняют ту же роль: передают данные из контроллера в представление. Однако эти переменные имеют разный тип. Как уже было сказано – переменная ViewBag это переменная типа dynamic, а переменная ViewData является словарём, в котором данные имеет тип object и доступны по ключу типа string. Когда-то очень давно, на одном из собеседований мне задали этот вопрос, и я не знал на него ответа. С тех пор прошло много времени, но разницу между этими двумя переменными я запомнил на всё жизнь.