Understanding the null coalescing operator (??)

2019-02-17 00:28发布

问题:

I have a custom WebControl which implements a .Value getter/setter returning a Nullable<decimal>

It's a client-side filtered textbox (a subclass of TextBox with included javascript and some server side logic for setting/getting the value)

Here is the getter & the setter from that control:

public decimal? Value
{
    get
    {
        decimal amount = 0;
        if (!decimal.TryParse(this.Text, NumberStyles.Currency, null, out amount))
        {
            return null;
        }
        else
        {
            return amount;
        }
    }
    set
    {
        if (!value.HasValue)
        {
            this.Text = "";
        }
        else
        {
            this.Text = string.Format("${0:#,##0.00}", value);
        }
    }
}

The problem that I'm seeing is that the output from this statement:

decimal Amount = uxAmount.Value ?? 0M;

I am seeing Amount being set to "0" when uxAmount.Value returns 10000.

This worked as I expected (excuse the change in casing):

decimal? _Amount = uxAmount.Value;
decimal amount = _Amount ?? 0;

I have also seen this behaviour (recently) when calling a UDF function defined on a Linq2Sql data context along with the null coalescing operator, that is I knew my UDF call returned the expected value but I was getting the RHS value instead.

Further confusing me, if I evaluate uxAmount.Value in the watch, I get 10000 of type Nullable<decimal>.

Here are some expressions I've tried:

decimal? _Amount = uxAmount.Value; //10000
decimal amount = _Amount ?? 0; //10000
decimal amount2 = _Amount ?? 0M; //10000
decimal Amount = uxAmount.Value ?? 0M; //0

Then I added this expression following the above 4

decimal amount3 = (uxTaxAmount.Value) ?? 0M;

Now

decimal Amount = uxAmount.Value ?? 0M; //10000
decimal amount3 = (uxAmount.Value) ?? 0M; //0

It seems like the last call is always 0, but the value of uxAmount.Value (which is parsed out of .Text as per above getter/setter using a TryParse is stable. I'm stopped at a breakpoint and there's no other threads that could manipulate this value.

Note the use of the M suffix to force the constant to decimal as it was integer and I suspected a type conversion issue.

Any ideas?

The value of both the LHS and RHS appear to be stable and known.

--edit-- some screengrabs from VS2010

回答1:

(This answer was constructed from my comments above.)

Are you sure the debugger dsiplays this correctly to you? Have you tried stepping some lines further down to make sure you have the updated value of amount3?

I'm sure it's just an issue with the debugger. Sometimes you have to step a little further. Maybe the translated code (IL) has some optimizations that confuse the debugger (or what would I know). But without the debugger, the value will be updated exactly when you expect it.

I've seen other experienced developers being confused by similar situations, so I know the debugger sometimes is "one line of code" behind when looking at an assignment to a local variable. Maybe someone can find a link discussing that?



回答2:

Take a look at this similar question

using coalescing null operator on nullable types changes implicit type

why not just do

decimal amount = uxTaxAmount.Value.HasValue ? uxTaxAmount.Value.Value : 0M

This isn't the right answer to the original posters problems given recent edits and comments.