views:

517

answers:

10

This seems like a bug to me...

I accept that automatic properties, defined as such:

public decimal? Total { get; set; }

Will be null when they are first accessed. They haven't been initialized, so of course they are null.

But, even after setting their value through +=, this decimal? still remains null. So after:

Total += 8;

Total is still null. How can this be correct? I understand that it's doing a (null + 8), but seems strange that it doesn't pick up that it means it should just be set to 8...

Addendums:

I made the "null + 8" point in my question - but notice that it works with strings. So, it does null + "hello" just fine, and returns "hello". Therefore, behind the scenes, it is initializing the string to a string object with the value of "hello". The behavior should be the same for the other types, IMO. It might be because a string can accept a null as a value, but still, a null string is not an initialized object, correct?

Perhaps it's just because a string isn't a nullable...

+4  A: 
Null + 8 = Null

You'll need to set it with zero before.

CD
Yeah, I know that's the solution, I just think it's dumb. :)
Sam Schutte
why is it dumb? null != 0
Francis B.
Because it's counter-intuitive, at least to me.If it were my compiler, I would have made the assumption (especially with something like an _automatic_ property) that if you have an uninitialized value and you add something to it, it would just pick up the added value. But, perhaps there are some cases where this kind of assumption would cause problems, who knows.
Sam Schutte
You do *NOT* have an uninitialized value. You have a value initialized to null.
Eric Lippert
+5  A: 

null means unknown value,

unknown value + known value = still unknown value
RaYell
I understand what a null is, but it seems to me that it would be a safe assumption that null + 8 would equal 8. But, I guess I'm just wrong. :)
Sam Schutte
You are wrong indeed :) If `null` was equal to `0` what would be the point of having a `null`? Notice that if you define your property as a non-nullable type (remove the `?` sign) it will be given the default value for decimal type which AFAIK is 0 and you will be able to use your `+= 8` code.
RaYell
Haha - :) Yeah, I've noticed the default value with auto-props before. I'm not saying that null should be equal to zero, just that if you have an initialized value and you add it to a null, it makes sense to me that the field would just pick up the initialized value.
Sam Schutte
Do you really would like it to work like this? Let me give you a real life example why this is wrong: lets assume you have some money in your wallet (however you are not sure how much is that, can be $20, can be $2000, this is our `null`). Now someone gives you extra $500. If this would work as you described your total money should be $500 now. I doubt you would like that :)
RaYell
I see where you're going, but I'm not sure that your example lines up with what null really is. What I mean is, it's not that null represents some "unknown value", such as "X". If someone told me I have X dollars and I added $500, yes, I would not like ending up with $500. But I don't think null represents "X", it represents (as they would say in VB), Nothing. So it's not the case that there's some unknown memory location that _might_ hold a value - null is very clearly nothing. So in that case, if I have nothing and I added $500, I'm ok with having $500. We're splitting hairs of course.
Sam Schutte
A: 

to set the value of the Total is just

Total = 8;

I would recommend reading up on Nullable Types to understand how they work. You can check to see if the property has a value by using HasValue.

From MSDN:

Operators

The predefined unary and binary operators and any user-defined operators that exist for value types may also be used by nullable types. These operators produce a null value if the operands are null; otherwise, the operator uses the contained value to calculate the result. For example:

int? a = 10;
int? b = null;

a++;         // Increment by 1, now a is 11.
a = a * 10;  // Multiply by 10, now a is 110.
a = a + b;   // Add b, now a is null.
Russ Cam
Thanks. I used nullable types quite a bit, and have run into this issue with automatic property initialization quite a bit, but never bothered to ask why. I suppose it makes sense, though I don't know if I would have done it that way...
Sam Schutte
There is a good section on Nullable Types in C# in Depth, amongst other interesting details of mainly C#3, but also a little C#1 and a bit more on C#2. A great book indeed.
Russ Cam
The whole idea of nullable types is that you can assign null values to value types :)
Russ Cam
Yes, of course.
Sam Schutte
Russ kept his unicorn! :)
Phil
+2  A: 

From MSDN:

When you perform comparisons with nullable types, if the value of one of the nullable types is null and the other is not, all comparisons evaluate to false except for != (not equal). It is important not to assume that because a particular comparison returns false, the opposite case returns true.

So, it works as intended.

Francis B.
+2  A: 

Here's a one-liner to initialize it on the first call and increment it afterwards:

    public void InitializeOrIncrement(decimal value)
    {
        // if Total is null then initialize, otherwise increment
        Total = (Total == null) ? value : Total + value;
    }

    public decimal? Total { get; set; }
STW
+17  A: 
public decimal? Total { get; set; }

Think of null as "unknown value". If you have an unknown quantity of something and you add 8 more, how many do you have now?

Answer: unknown.

Operations on Nullable Variables

There are cases where operations on unknown values give you knowable results.

public bool? State { get; set; }

The following statements have knowable solutions even though they contain unknown values:

State = null;
nextState = State & false;         // always equals false
nextState = State & true;          // still unknown (null)

nextState = State | true;          // always true
nextState = State | false;         // still unknown (null)

See the pattern?

Of course, if you want Total to be equivalent (equal) to 0 when it is null, you can use the null coalescing operator and write something like this:

Total = (Total ?? 0) + 8;

That will use the value of Total in your equation unless it is null, in which case it will use the value 0.

Robert Cartaino
Yeah, I suppose this makes sense. Just seems funky. :)
Sam Schutte
Oh, and good call about the coalescing operator.
Sam Schutte
A: 

Null isn't the same as zero. Zero plus eight is eight... but null plus eight? Always null. Just like infinity plus anything is still infinity - it's undefined.

You'll find that this is universally true of null. Every database (at least that I've ever worked with) will give you the same result.

womp
The infinity + x = infinity is a good way to put it.
Sam Schutte
A: 

public decimal? Total { get; set; }

Would something like this work? Some sort of automatic initalization if the value isn't yet set.

public decimal? Total
{
  get { return this.totalValue;}
  set
  { 
     if(totalValue == null) { this.totalValue = 0;}
     this.totalValue = value;
  }
}

private decimal? totalValue;
Ian
...but if you're not using auto-implemented properties then why not just initialize your backing field as part of it's declaration? `private decimal? totalValue = 0;`
STW
Yeah, let's face it, the only reason I'm using automatic properties at all is to be lazy about typing. (Not object typing - just finger typing!) If I were going to split it out, I would just default it to 0.
Sam Schutte
Yooder, because he might not want the value to be 0, he may wish it to be null... Robert, no actually if someone sets Total to null, it will be set to 0, and then reset to null in this case, as 'value' would be null. 0 is just used as a lazy initalizer.
Ian
+1  A: 

I know that it makes sense to do

public decimal? Total { get; set; }

Total = (Total ?? 0) + 8;

but wouldnt it just be easier to do :

public decimal Total { get; set; }

initial value of Total is 0

Hurricanepkt
Yeah, this is actually what I ended up changing my code to. For a running total, it really doesn't make sense for the total to ever by null really, if you think about it. It makes sense for the items being added in to potentially be null, but the total can probably always be >= 0. Interesting discussion though.
Sam Schutte
A: 

As other people have pointed out, null is not equal to zero. Although it may seem more convenient for a null integer to default to zero in the long run it is likely to produce weird results that you may not spot until it is too late.

As a quick example, say one of your data feeds fails and populates your result set with nulls. Your calculations will treat the nulls as zeros and continue to produce results. As numbers are still coming out, even though they are likely wrong, you might never notice that something has gone critically wrong.

Phil