Аномалия при использовании "var" и "dynamic"
Я столкнулся с проблемой аномалии, где в первый раз, используя ключевое слово var
, меня немного.
Возьмите этот очень простой метод
public static Int32? GetNullableInt32(Int32 num)
{
return new Nullable<Int32>(num);
}
Теперь мы можем вызвать этот метод с параметром dynamic
, и все будет работать так, как ожидалось.
public static void WorksAsAdvertised()
{
dynamic thisIsAnInt32 = 42;
//Explicitly defined type (no problems)
Int32? shouldBeNullableInt32 = GetNullableInt32(thisIsAnInt32);
Console.Write(shouldBeNullableInt32.HasValue);
}
Однако, объявив shouldBeNullableInt32
, используя неявное типирование, результаты далеки от того, что я ожидал бы.
public static void BlowsUpAtRuntime()
{
dynamic thisIsAnInt32 = 42;
//Now I'm a dynamic{int}... WTF!!!
var shouldBeNullableInt32 = GetNullableInt32(thisIsAnInt32);
//Throws a RuntimeBinderException
Console.Write(shouldBeNullableInt32.HasValue);
}
Вместо Nullable<Int32>
возвращаемое значение обрабатывается как динамический тип. И даже тогда базовый Nullable<T>
не сохраняется. Так как System.Int32
не имеет свойства с именем HasValue
, то бросается RuntimeBinderException
.
Я был бы ОЧЕНЬ любопытным, чтобы услышать от кого-то, кто может фактически объяснить, что происходит (не просто догадаться).
Два вопроса
- Почему
shouldBeNullableInt32
получает неявно введенный в качестве динамического, когда возвращаемый тип GetNullableInt32
явно возвращает Nullable<Int32>
?
- Почему базовый
Nullable<Int32>
не сохраняется? Почему вместо dynamic{int}
? (Ответ здесь: С# 4: Динамический и Nullable < gt;)
UPDATE
Оба ответ Рика Сладке и ответ Эрика Липперта в равной степени действительны. Прочитайте их оба:)
Ответы
Ответ 1
- Почему
shouldBeNullableInt32
получает неявно введенный в качестве динамического, когда возвращаемый тип GetNullableInt32
явно возвращает Nullable<Int32>
?
Это потому, что, хотя нам очевидно, что GetNullableInt32
- это метод, который будет вызываться, из-за динамическое связывание, фактический метод, вызывающий вызов, откладывается до времени выполнения, потому что он вызывается с динамическим параметром. Может быть другая перегрузка GetNullableInt32
, которая лучше соответствует значению времени выполнения thisIsAnInt32
. Этот альтернативный метод, который не может быть известен до времени выполнения, может возвращать другой тип, чем Int32?
!
В результате компилятор из-за динамической привязки вместо статической привязки не может предположить, что тип возврата выражения находится во время компиляции, и поэтому выражение возвращает тип dynamic. Это можно увидеть, наведя курсор на var
.
Похоже, вы уже пришли к удовлетворительному объяснению своего второго вопроса:
Ответ 2
Ответ на Rick хорош, но просто для подведения итогов вы сталкиваетесь с последствиями двух основных принципов дизайна этой функции:
- , если вы запрашиваете динамическую привязку, вы получаете динамическое связывание.
- динамика - это просто объект, носящий забавную шляпу.
Первая проблема, которую вы идентифицируете, является следствием первого принципа проектирования. Вы просили проанализировать, что вызов будет отложен до выполнения. Компилятор сделал это. Это включает в себя отсрочку всего о вызове до времени выполнения, включая разрешение перегрузки и определение типа возврата. Тот факт, что у компилятора достаточно информации, чтобы угадать, что вы имели в виду, не имеет значения.
И если компилятор действительно догадывался о том, что вы имели в виду, то прямо сейчас вы задаете другой вопрос, а именно: "Я внес небольшое изменение в набор доступных методов, и вдруг компилятор изменил свой вывод тип для динамического, почему?" Это очень запутывает пользователей, когда поведение компилятора непредсказуемо.
(Все, что сказано, есть небольшое количество ситуаций, в которых компилятор скажет вам, что динамический код неверен. Бывают ситуации, когда мы знаем, что динамическое связывание всегда будет терпеть неудачу во время выполнения, и мы можем рассказать вам о их во время компиляции, а не ждать, пока ваш тестовый пример не сработает.)
Вторая проблема, которую вы идентифицируете, является следствием второго принципа проектирования. Поскольку динамика - это просто объект, носящий смешную шляпу, и потому, что поле nullables указывает либо на нулевую ссылку, либо на тип с нулевым значением в виде столбца, то нет такой вещи, как "динамическая нулевая".