2013-05-29 2 views
41

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

public static void Main(string[] args) 
{ 
    int? n = 1; 
    int i = 1; 
    n = ++n - --i; 
    Console.WriteLine("Without Nullable<int> n = {0}", n); //outputs n = 2 

    n = 1; 
    i = 1; 
    n = ++n - new Nullable<int>(--i); 
    Console.WriteLine("With Nullable<int> n = {0}", n); //outputs n = 3 
    Console.ReadKey(); 
} 

I exepcted оба выхода должны быть одинаковыми и равными 2, но, как ни странно, это не так. Может кто-нибудь объяснить, почему?

EDIT: Хотя код для создания этого «странного» поведения, по общему признанию надуманный, это выглядит как ошибка в C# компилятора, хотя, казалось бы, неважные и причина, кажется, встраиваемыми new, как James указывал на начальном этапе. Но поведение не ограничивается операциями. Вызовы метода ведут себя точно так же, т. Е. Их вызывают дважды, когда их нужно вызывать только один раз.

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

public static void Main() 
    { 
     int? n = 1; 
     int i = 1; 
     n = n - new Nullable<int>(sideEffect(ref i)); 
     Console.WriteLine("With Nullable<int> n = {0}", n); 
     Console.ReadKey(); 
    } 

    private static int sideEffect(ref int i) 
    { 
     Console.WriteLine("sideEffect({0}) called", i); 
     return --i; 
    } 

Конечно, выход есть 2, когда он должен быть 1 и "sideEffect(i) called" печатается дважды.

+3

Это что-то делать с фактически 'новый Nullable (--i)' инлайн, потому что если вы назначаете это к переменной *, тогда * выполните операцию, это нормально. Что также странно, если я поставил точку останова на эту строку, а затем положил в watch 'new Nullable (--i)' it уменьшает 'i' (что имеет смысл), но * увеличивает * конечный результат на тот же номер: s – James

+0

@James: Wierd ... это, похоже, не является желательным поведением ... – InBetween

+0

ну, откуда я стою, я бы сказал нет, или, по крайней мере, не то, что я ожидал бы. Я бы ожидал, что новая «нулевая переменная» будет создана «на лету», которая равна 0. – James

ответ

20

EDIT: Это было подтверждено как ошибка в компиляторе командой. Это зафиксировано в Рослине. В качестве обходного пути используйте бросок (int?)(--i), чтобы остановить появление ошибки или, в первую очередь, не сделать ее явно Nullable<int>.

Первый блок кода генерирует следующее в отражателе:

int? nullable3; 
int? nullable = 1; 
int num = 1; 
int? nullable2 = nullable; 
nullable2 = nullable = nullable2.HasValue 
    ? new int?(nullable2.GetValueOrDefault() + 1)  
    : ((int?) (nullable3 = null)); 
int num2 = --num; 
nullable = nullable2.HasValue 
    ? new int?(nullable2.GetValueOrDefault() - num2) 
    : ((int?) (nullable3 = null)); 
Console.WriteLine("Without Nullable<int> n = {0}", nullable); 

втором следующее:

nullable = 1; 
num = 1; 
nullable2 = nullable; 
nullable2 = nullable = nullable2.HasValue 
    ? new int?(nullable2.GetValueOrDefault() + 1) 
    : ((int?) (nullable3 = null)); 
num2 = --num; 
nullable = nullable2.HasValue 
    ? new int?(nullable2.GetValueOrDefault() - --num) 
    : null; 
Console.WriteLine("With Nullable<int> n = {0}", nullable); 

Они более или менее такой же, вплоть до присвоения nullable. Он запускает --num дважды, заставляя его запускать 2 - -1, в результате чего получилось 3.

Он также делает то же самое с выражениями, как i = ~i, но не с выражениями вызова метода ...

+0

Вызовы метода ведут себя точно так же. См. Мое редактирование. – InBetween

+0

Я пробовал вызов метода, но не смог его воспроизвести. Это был метод no-args, а не аргумент ref, может быть, это что-то с этим связано? – thecoop

+0

Не уверен, и я не могу проверить это сейчас. О 'ref', его не является частью проблемы. Я вложил его, чтобы сделать * visble *, что конечное значение 'n' неверно. Если я не передал «i» по ссылке, даже если вы вызовете 'sideEffects (i)' дважды, 'i' на callsite будет только уменьшаться один раз, и код, по-видимому, работает нормально.Очевидно, что это не так, поскольку 'sideEffect (i), называемый', печатается дважды в консоли, с или без 'ref'. – InBetween

4

Это довольно интересная проблема, от того, что я могу видеть, компилятор появляется оценить заявления --/++ более чем один раз. Например, следующее:

n = ++n - new Nullable<int>(i++) 

приводит n становится 0 (который можно было бы ожидать), но i теперь 3 (который можно было бы ожидать, чтобы быть 2). Однако, если я

n = ++n - new Nullable<int>(i); 

Тогда я получить ожидаемый результат (n = 1 и i = 1)

Я могу только предположить, что это как-то связано с new Nullable вызова, находящегося в режиме реального времени. Я действительно не вижу в этом проблемы, поскольку это, вероятно, не будет считаться вашим повседневным кодом, однако, по-моему, это похоже на ошибку с компилятором.

+0

В моем примере cshtml не используется компилятор, поэтому он не может быть компилятором –

+0

@ThomasFonseca, так как он запускает код C# на компьютере без компилятора? – thecoop

+0

@ThomasFonseca ваш cshtml * делает * использовать компилятор, один ASP.NET. – James

-2

Ну его наиболее странным, я пытался разыскать фактический код от «-», но я не могу, но если вы

  n = 1; 
     i = 1; 
     n = ++n - new Nullable<int>(i--); 
     Console.WriteLine("With Nullable<int> n = {0}", n); //outputs n = 2 
     Console.ReadKey(); 

Он выдает, как и ожидалось.

редактировать: Все раскрывается:

http://msdn.microsoft.com/en-US/library/wc3z3k8c(v=vs.80).aspx

+4

Ожидаемое значение 'n = ++ n - new Nullable (i -);' равно 1, а не 2. –

+1

Нет, вы получаете неправильный вывод 2: Если вы используете постдекремент, то правильный вывод должно быть '1'. И ссылка, извините, не очень помогает вашему делу;) – InBetween

+0

Извините, что вы правы, получая мой префикс и постфикс неправильным образом :( – pingoo

-3

Это происходит потому, что на этой линии:

n = ++n - new Nullable<int>(--i); 

я становится -1 и 2 - (-1) = 3;

Причина, по которой она становится отрицательной 1, заключается в том, что вы вводите объект nulluable, который инициализируется 0, а затем вычитает один (i).

вы можете запустить этот код как * .cshtml файл в вашем браузере:

@{ 

    int? m = 1; 
    int i = 1; 
    m = ++m - --i; 
    //MessageBox.Show("Without Nullable<int> n = {0}", n); //outputs n = 2 

    int? n = 1; 
    i = 1; 
    n = ++n - new Nullable<int>(--i); 
    //MessageBox.Show("With Nullable<int> n = {0}", n); //outputs n = 3 

} 

<!DOCTYPE html> 

<html lang="en"> 
<head> 
    <meta charset="utf-8" /> 
    <title></title> 
</head> 
<body> 
    <h2>m = @m</h2> 
    <h2>n = @n</h2> 
</body> 
</html> 
+0

Ну, это странный способ взглянуть на проблему Я предопределяю * аргумент * конструктора 'Nullable ', я определенно не уменьшаю значение 'Nullable '. Я ожидал, что '-i' вернет' 0' и 'new Nullable (0) 'разрешено' 0'. Очевидно, что этого не происходит. – InBetween

+0

Я не сказал, что вы вычитал один из объекта с нулевым значением. Я сказал, что вы вычитаете его из инициализатора, в этом случае i. –

+5

BTW, вы не может запускать код CSHTML в браузере. – SLaks

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