开发者

null coalescing order of operation

开发者 https://www.devze.com 2023-03-10 19:54 出处:网络
I\'m getting strange results from this method: public static double YFromDepth(double Depth, double? StartDepth, double? PrintScale)

I'm getting strange results from this method:

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

When I pass a null into St开发者_如何学GoartDepth, the coalescing is failing because "Depth - StartDepth" is being evaluated seeming by first converting StartDepth to a default of 0 (downgraded?) instead of first looking at whether it is null and substituting in the Globals.StartDepth instead.

Is this a known thing? I was able to make this work by adding parenthesis, but I really didnt expect things to work this way.


No, it's not a bug. It's the specified order of precedence - the binary - operator has higher precedence than ??, so your code is effectively:

return ((Depth - StartDepth) ?? Globals.StartDepth) * 
          PrintScale ?? Constants.YPixelsPerUnit;

If you don't want that precedence, you should specify it explicitly:

return (Depth - (StartDepth ?? Globals.StartDepth)) * 
          PrintScale ?? Constants.YPixelsPerUnit;

Personally I would expand the method to make it clearer:

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


As @Jon Skeet, this is an issue with precedence and can be solved by defining the correct precedence explicitly with parentheses. (e.g. (Depth - (StartDepth ?? Globals.StartDepth)) * PrintScale ?? Constants.YPixelsPerUnit;)

This concept is not immediately obvious, and the way precedence, associativity and order of evaluation work in C# is not always intuitive.

Eric Lippert explains these concepts very well in his article Precedence vs Associativity vs Order. I highly recommend reading that article. The following are the most critical excerpts:

Precedence

Precedence rules describe how an underparenthesized expression should be parenthesized when the expression mixes different kinds of operators. For example, multiplication is of higher precedence than addition, so 2 + 3 x 4 is equivalent to 2 + (3 x 4), not (2 + 3) x 4.

Associativity

Associativity rules describe how an underparenthesized expression should be parenthesized when the expression has a bunch of the same kind of operator. For example, addition is associative from left to right, so a + b + c is equivalent to (a + b) + c, not a + (b + c). In ordinary arithmetic, these two expressions always give the same result; in computer arithmetic, they do not necessarily. (As an exercise can you find values for a, b, c such that (a + b) + c is unequal to a + (b + c) in C#?)

Order of Evaluation

Order of evaluation rules describe the order in which each operand in an expression is evaluated. The parentheses just describe how the results are grouped together; "do the parentheses first" is not a rule of C#. Rather, the rule in C# is "evaluate each subexpression strictly left to right".


It all depends on what operator precedence is set in the language. IIRC ? and ?? have pretty low precedence.


As can be seen from the order of precedence of the different operators, the null coalesce is much lower then - or *.


I think it is a problem with brackets....Try this:

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

HTH

0

精彩评论

暂无评论...
验证码 换一张
取 消