Аномалия при использовании 'var' и 'dynamic' - PullRequest
27 голосов
/ 16 сентября 2011

Я столкнулся с небольшой аномалией, когда впервые за все время использовал ключевое слово var bit me.

Возьмем этот очень простой метод

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

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>, возвращаемое значение get обрабатывается как динамический тип.И даже тогда базовый Nullable<T> не сохранился.Поскольку System.Int32 не имеет свойства с именем HasValue, выбрасывается RuntimeBinderException.

Мне было бы ОЧЕНЬ любопытно услышать от кого-то, кто действительно может объяснить, что происходит (не толькоугадайте).

Два вопроса

  1. Почему shouldBeNullableInt32 получает , неявно типизированный как динамический , когда возвращаемый тип GetNullableInt32 явно возвращает Nullable<Int32>?
  2. Почему базовый Nullable<Int32> не сохранился?Почему вместо dynamic{int}? ( Здесь ответили : C # 4: Динамический и обнуляемый <> )

ОБНОВЛЕНИЕ

Ответ Рика Сладки и Ответ Эрика Липперта одинаково действительны.Пожалуйста, прочитайте их оба:)

Ответы [ 2 ]

19 голосов
/ 16 сентября 2011
  1. Почему shouldBeNullableInt32 получает , неявно типизированный как динамический , когда тип возврата GetNullableInt32 явно возвращает Nullable<Int32>?

Это потому, что для нас очевидно, что GetNullableInt32 - это метод, который будет вызываться из-за динамического связывания , фактического метода, который делает get call отсроченным до времени выполнения, потому что он вызывается с динамическим параметром. Может быть другая перегрузка GetNullableInt32, которая лучше соответствует значению времени выполнения thisIsAnInt32. Этот альтернативный метод, который не может быть известен до времени выполнения, может вернуть какой-то другой тип, кроме Int32?!

В результате компилятор из-за динамического связывания вместо статического связывания не может предположить, какой тип выражения выражения находится во время компиляции, поэтому выражение возвращает тип dynamic . Это можно увидеть, завис над var.

Вы, кажется, уже пришли к удовлетворительному объяснению вашего второго вопроса здесь:

17 голосов
/ 16 сентября 2011

Хороший ответ Рика, но, если подвести итог, вы столкнулись с последствиями двух основных принципов проектирования этой функции:

  1. если вы запрашиваете динамическое связывание, тогда вы получаете динамическое связывание .
  2. динамический - это просто объект в смешной шляпе .

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

И если компилятор действительно догадался о том, что вы имели в виду, то сейчас вы зададите другой вопрос, а именно: «Я внес небольшое изменение в набор доступных методов, и вдруг компилятор изменил свой вывод тип динамический, почему? Это очень сбивает с толку пользователей, когда поведение компилятора непредсказуемо.

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

Вторая выявленная вами проблема является следствием второго принципа проектирования. Поскольку динамический объект представляет собой просто забавную шляпу, а поскольку в качестве значения NULL указывается пустая ссылка или тип значения, не имеющий значения NULL, в штучной упаковке не существует такого понятия, как «динамическое значение NULL».

...