2012-02-21 3 views
13

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

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

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 и идентификатор пользователя все еще динамический, что означает, что я тогда не может использовать их в более поздних лямбда-выражений. Очевидно, что я могу исправить код с помощью:

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

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

Thanks

+2

«Кто-нибудь может объяснить это мне?» вероятно, должно быть: «Может ли Эрик Липперт объяснить это нам?» – dasblinkenlight

+0

Это меня удивило. Я не поверил тебе, пока сам не пробовал! :-) – Simon

+0

Другим способом решения этой проблемы является принудительный тип 'string' в аргументе *' DateTime.Parse() ', например:' var startDate = DateTime.Parse ((string) settings.startDate) ' – dasblinkenlight

ответ

13

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

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

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

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

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

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

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

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

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

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

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

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

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

3

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

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

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

Вы только что объединили эти два.

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

+2

Для меня ваш первый факт ** является ** чрезвычайно удивительным. Если 'DateTime.Parse' возвращается успешно, он ** будет ** возвращать' string'. Не так ли ?! – AakashM

+0

Это будет, но все динамические вызовы работают так. В вашем случае это просто, но в перегруженных методах его невозможно будет решить, поэтому он никогда не будет решен. –

+0

@AakashM Первый факт объясняется в 7.2.2 спецификации языка C# - см. Мой ответ для деталей. –

2

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

призывание-выражение динамически связанное (§7.2.2), если по меньшей мере одно из следующих условий:

• В первичной экспрессии имеет тип во время компиляции 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 имеет только одну перегрузку, и даже если бы она не была, но все ее перегрузки имели тот же тип возврата, что в этом случае он должен иметь возможность фигурировать введите тип времени компиляции. Это было бы справедливым моментом и, вероятно, лучшим Эриком Липпертом. Я задал отдельный вопрос, чтобы получить представление об этом: Why does a method invocation expression have type dynamic even when there is only one possible return type?.

1

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

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

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

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); 

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

 Смежные вопросы

  • Нет связанных вопросов^_^