Подтвердить что ты не робот

Не понимайте поведение оператора до декремента с помощью типа 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();
}

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

РЕДАКТИРОВАТЬ: Хотя код для создания этого "странного" поведения, по общему признанию, был надуман, он выглядит как ошибка в компиляторе С#, хотя, казалось бы, неважно, и причина, по-видимому, является встроенной 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" распечатан дважды.

4b9b3361

Ответ 1

РЕДАКТИРОВАТЬ: Это было подтверждено как ошибка в компиляторе командой. Это зафиксировано в Рослине. В качестве обходного пути используйте листинг (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, но не с выражениями вызова метода...

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

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

edit: Все обнаружено:

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

Ответ 4

Это потому, что на этой строке:

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

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>