不理解Nullable类型的预减运算符行为

本文关键字:运算符 Nullable 类型 不理解 | 更新日期: 2023-09-27 18:15:59

好的,这对你们中的一些人来说可能是显而易见的,但我对我从这段相当简单的代码中得到的行为感到困惑:

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,但奇怪的是它们不是。有人能解释一下原因吗?

EDIT:虽然生成这种"奇怪"行为的代码无可否认是人为的,但它看起来确实像c#编译器中的错误,尽管看起来不重要,原因似乎是内联new,正如James最初指出的那样。但这种行为并不局限于操作。方法调用的行为完全相同,也就是说,当它们只应该被调用一次时,它们被调用了两次。

考虑下面的repro:

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

果然应该是1,输出却是2, "sideEffect(i) called"打印了两次

不理解Nullable类型的预减运算符行为

EDIT:这已经被团队确认为编译器中的一个bug。它被固定在罗斯林。作为一种解决方法,使用强制转换(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这样的表达式做同样的事情,但不包括方法调用表达式…

这是一个相当有趣的问题,从我可以看到编译器出现计算--/++语句不止一次。例如:

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

导致n变成0(这是您所期望的),但i现在是3(这是您所期望的2)。但是,如果我这样做

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

然后我得到预期的结果(n = 1i = 1)

我只能假设这是在某种程度上new Nullable呼叫内联有关。我真的不认为这是一个大问题,因为这可能不会被认为是你的日常代码,然而,在我看来,这似乎是一个错误与编译器。

最奇怪的是,我试图追踪'——'的实际代码,但我无法,但如果你这样做

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

按预期输出。

编辑:All is revealed:

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

这是因为在这一行:

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

i变为-1,2 - (-1)= 3;

它变为- 1的原因是您正在创建一个可空对象,该对象初始化为0,然后减去1 (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>