Выбирает ли C # неправильный тип для var при разборе динамического объекта? - PullRequest
13 голосов
/ 21 февраля 2012

Я использую следующий код для преобразования Json в динамический объект. Когда я использую DateTime.Parse для свойства моего динамического типа, я ожидаю, что var предположит, что его тип - DateTime ... вместо этого он остается динамическим. Это не может быть правдой, не так ли?

Полный пример ниже.

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

var startDate = DateTime.Parse(settings.startDate);
var endDate = DateTime.Parse(settings.endDate);
var userId = int.Parse(settings.userId);

startDate, endDate и userId все еще динамичны, что означает, что я не могу использовать их в более поздних лямбда-выражениях. Очевидно, я могу исправить код с помощью:

DateTime startDate = DateTime.Parse(settings.startDate);
DateTime endDate = DateTime.Parse(settings.endDate);
int userId = int.Parse(settings.userId);

.. но кажется, что компилятор делает "неправильное предположение". Кто-нибудь может мне это объяснить?

Спасибо

Ответы [ 4 ]

13 голосов
/ 21 февраля 2012

.. но кажется, что компилятор делает "неправильное предположение". Кто-нибудь может мне это объяснить?

Когда вы используете dynamic, все выражение обрабатывается во время компиляции как динамическое выражение , что заставляет компилятор обрабатывать все как динамическое и получать привязку во время выполнения.

Это объясняется в 7.2 спецификации языка C #:

Когда динамические выражения не задействованы, в C # по умолчанию используется статическое связывание, что означает, что типы составных выражений во время компиляции используются в процессе выбора. Однако, когда одно из составных выражений в операциях, перечисленных выше, является динамическим выражением, операция вместо этого динамически связана.

Это в основном означает, что большинство операций (типы перечислены в разделе 7.2 спецификации), которые имеют любой элемент, объявленный как dynamic, будут оцениваться как dynamic, а результатом будет dynamic.

В вашем случае это утверждение:

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

Используется динамическое, поэтому оно рассматривается как динамическое выражение. Поскольку «вызов метода» является одной из операций C #, подлежащих привязке (7.2), компилятор обрабатывает это как динамическую границу, что приводит к следующему значению:

dynamic settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

Это, в свою очередь, приводит к тому, что выражения DateTime.Parse становятся динамически связанными, что, в свою очередь, заставляет их возвращать dynamic.

Ваше «исправление» работает, когда вы делаете DateTime startDate = DateTime.Parse(settings.startDate);, потому что это вызывает неявное динамическое преобразование (описанное в разделе 6.1.8 спецификации) результата метода DateTime.Parse в DateTime:

Существует неявное динамическое преобразование из выражения динамического типа в любой тип T. Преобразование является динамически связанным (§7.2.2), что означает, что неявное преобразование будет выполняться во время выполнения из типа времени выполнения выражения в T. Если преобразование не найдено, генерируется исключение времени выполнения.

В этом случае преобразование допустимо, поэтому с этого момента вы фактически переключаете все обратно на статическое связывание.

3 голосов
/ 21 февраля 2012

Не думаю, что это особенно удивительно.

DateTime.Parse(<dynamic>) будет оцениваться как динамический.

DateTime startDate = <dynamic> выполняет присвоение времени выполнения из динамического в DateTime.

Вы только что соединили оба.

Компилятор не предполагает, что тип DateTime.Parse(<dynamic>) является чем-то отличным от динамического, но он достаточно умен, чтобы понять, что если вы присваиваете этому значению DateTime, то предполагаете, что это успешно, у вас остается DateTime.

2 голосов
/ 21 февраля 2012

Это согласно спецификации.См. §7.6.5:

Выражение вызова является динамически связанным (§7.2.2), если выполняется хотя бы одно из следующего:

primary-выражение имеет тип времени компиляции dynamic.

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

Рассмотрим следующий сценарий:

class Foo {
    public int M(string s) { return 0; }
    public string M(int s) { return String.Empty; }
}

Foo foo = new Foo();
dynamic d = // something dynamic
var m = foo.M(d);

Каким должен быть тип времени компиляции m?Компилятор не может сказать, потому что он не будет знать до времени выполнения, какая перегрузка Foo.M вызвана.Таким образом, он говорит, что m является динамическим.

Теперь вы можете сказать, что он должен быть в состоянии выяснить, что DateTime.Parse имеет только одну перегрузку, и даже если он этого не сделал, но все его перегрузки имеюттот же тип возвращаемого значения, который в этом случае должен быть в состоянии определить тип времени компиляции.Это было бы справедливо, и, вероятно, лучше всего Эрик Липперт.Я задал отдельный вопрос, чтобы разобраться в этом: Почему выражение вызова метода имеет динамический тип, даже если существует только один возможный тип возвращаемого значения? .

1 голос
/ 21 февраля 2012

Здесь есть два разных понятия

  1. динамический : любой тип, разрешенный в время выполнения
  2. var : неявная статическая типизация, выполняемая во время компиляции

Так что, если вы делаете

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
var startDate = DateTime.Parse(settings.startDate);

во время компиляции он разрешается в динамический тип, а во время выполнения - в конкретный тип. Компилятор проверяет правую часть new JavaScriptSerializer().Deserialize<dynamic>(json);, которая возвращает динамический. Во время компиляции это указывает компилятору отбросить все проверки безопасности типов и использовать их до времени выполнения.

Этот код

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
DateTime startDate = DateTime.Parse(settings.startDate);

явно говорит, что динамический объект имеет конкретный тип, поэтому компилятор может вывести тип, все его методы и выполнить статическую проверку типа во время компиляции.

...