空值合并运算符的优先级

5
我从这个方法中得到了奇怪的结果:
public static double YFromDepth(double Depth, double? StartDepth, double? PrintScale)
{               
    return (Depth - StartDepth ?? Globals.StartDepth) * PrintScale ?? Constants.YPixelsPerUnit ;
}

当我将null传入StartDepth时,由于“Depth-StartDepth”似乎首先将StartDepth转换为默认值0(降级?),而不是首先查看它是否为空并替换Globals.StartDepth,因此合并失败。
这是一个已知的问题吗?我通过添加括号使其工作,但我真的没有预料到会这样工作。
5个回答

10

不,这不是一个bug。这是特定的运算符优先级规则 - 二元运算符-??有更高的优先级,所以你的代码实际上是:

return ((Depth - StartDepth) ?? Globals.StartDepth) * 
          PrintScale ?? Constants.YPixelsPerUnit;
如果你不想要那个优先级,你应该明确地指定它:
return (Depth - (StartDepth ?? Globals.StartDepth)) * 
          PrintScale ?? Constants.YPixelsPerUnit;

个人而言,我会扩展这个方法以使其更清晰:

double actualStartDepth = StartDepth ?? Globals.StartDepth;
double actualScale = PrintScale ?? Constants.YPixelsPerUnit;
return (depth - actualStartDepth) * actualScale;

是的,我在计算中添加了括号,现在它可以正确运行了。不确定为什么,但我一开始认为这是一个非常早期的操作,可能是因为我预期它直接从可空类型而不是表达式等结果中工作。谢谢。 - Brady Moritz

3
如@Jon Skeet所说,这是一个优先级问题,可以通过使用括号显式地定义正确的优先级来解决。(例如(Depth - (StartDepth ?? Globals.StartDepth)) * PrintScale ?? Constants.YPixelsPerUnit;)。
这个概念不是立即显而易见的,C#中的优先级、结合性和求值顺序并不总是直观的。
Eric Lippert在他的文章Precedence vs Associativity vs Order中很好地解释了这些概念。我强烈推荐阅读这篇文章。以下是最关键的摘录:

优先级

优先级规则描述当表达式混合不同类型的运算符时,未加括号的表达式应该如何加括号。例如,乘法的优先级高于加法,因此2+3×4等价于2+(3×4),而不是(2+3)×4。

结合性

结合律规则描述了当表达式具有一堆相同类型的运算符时,一个未括号化的表达式应该如何被括号化。例如,加法从左到右是结合的,因此a + b + c等同于(a + b) + c,而不是a + (b + c)。在普通算术中,这两个表达式总是给出相同的结果;在计算机算术中,它们不一定相同。(作为练习,你能找到C#中(a + b) + c不等于a + (b + c)的值吗?)
评估顺序规则描述了表达式中每个操作数的评估顺序。括号只描述了结果如何分组在一起;“先做括号”不是C#的规则。相反,C#的规则是“严格从左到右评估每个子表达式”。

0

这完全取决于语言中运算符优先级的设置。如果我没记错,问号和双问号的优先级相当低。


0

从不同运算符的优先顺序可以看出,空合并运算符比-*低得多。


0

我认为这是一个括号的问题... 试试这个:

public static double YFromDepth(double Depth, double? StartDepth, double? PrintScale)
{               
    return (Depth - (StartDepth ?? Globals.StartDepth)) * (PrintScale ?? Constants.YPixelsPerUnit) ;
}

HTH


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接