Why does >= return false when == returns true for null values?
Asked Answered
P

8

78

I have two variables of type int? (or Nullable<int> if you will). I wanted to do a greater-than-or-equal (>=) comparison on the two variables but as it turns out, this returns false if both variables are null, while obviously the == operator returns true.

Can someone explain to me why that is logical because the semantical definition of the >= operator contains the word "or"?

Pamper answered 9/12, 2010 at 15:31 Comment(16)
Can you post the code that produces this weird behavior?Barrettbarrette
Actually, I would question whether it is appropriate for == to return true. I don't think that is appropriate at all. How can two variables whose value is unknown be certified as equal ?Quodlibet
@Charles, because, if they are of the same nulled type (e.g. int?), then their value is known. null.Wellmannered
@moo-juice, Not in my world... If your name is null, would you answer to that? null is not the same as "null". There's an old movie called "My name is Nobody" ("Who broke the glass, children? " ---- "Nobody" ) So who is "Nobody"? In the real world, (outside of the code internals), null means the value is unknown.... The data structure value may be known (it is Null) but the real problem domain entity/value that the variable is there to represent is Not known..Quodlibet
@Charles, love the analogy!. But who didn't break the glass?Wellmannered
I think that's why the IsNull/IsNothing methods were conceived, but changing the == check would have broken a lot of code, so it was left. @Charles brings up a good point, null is not a concrete value, but a representation of unknown, unknown cannot be equal to anything else, even unknown.Hodgepodge
@Moo-Juice, Didn't we not have a different argument last year at a conference ??Quodlibet
Why does "null" have to be regarded as a "nullable" type? If it's regarded as its own "null" type, with widening conversions to any nullable type, then the "==" operator could define that comparison between a null constant and a nullable type which happens to be null will be "true", while a comparison between two nullable types that happen to be null would be "null".Anastos
What would be the point of nullable unless you could compare null? The only point of null is to have a "Magic" variable that isn't a number for cases where the number isn't appropriate--or causing bugs.Inarch
It is true that in the real world null means unknown, but keep in mind that the syntax in C# for checking if a value is null is val == null. If C# had added a separate keyword for null-checking the way SQL does it, then that would not be the case.Tackett
There are, truly, two different overloaded uses of the keyword null in C#. It's use for nullable types is identical to the use and meaning of null in SQL, and should have been treated that way. It's other use, (equivilent to a null pointer in C/C++) means that a reference variable is unassigned. Pity these two very different situations are not represented with a different keyword. The use of same keyword for both does engender some confusion.Quodlibet
As a thought experiment, imagine coding a "nullablle" reference type, exactly as the framework codes a nullable int, as a struct with an int field for the value and a boolean to hold whether it is null, with IsNull and HasValue properties, exactly like the real int?, but make it a class instead of a struct. A variable of this type would have both meanings of null associated with it. Before you newed up an instance of it, it would be null(Unassigned - a null pointer). After instantiating one, before assigning it a value, it's IsNull would be true, & it's HasValue would be falseQuodlibet
@Charles Bretana, null variables are not uninitialized, they are specifically initialized with the null value. which is also not "nothing" nor "uninitialized", it simply means they are not initialized with a "user value" but a "system value" indicating this particular state. Null is not lack of a value, it's only lack of a user value, null is a value in itself, that's why it can be compared to other values.Clint
@Pop, My apologies if I misread your post, but it's tone seems as if you believe you are somehow contradicting me. I searched this thread for where I might have used the phrase "null variables are not initialized", and could not find it. If I missed it, (I don't think so), my apologies, but, even if I did, that seems a poor excuse to misinterpret me. If you read the complete thread I don't see how you could have so clearly misunderstood the point I am making, which your post neither contradicts, nor contributes to in any substantive form.Quodlibet
@Charles Bretana, my mistake, you've written unknown and I've used uninitialized, I meant to say "unknown"Clint
@Pop, been there... ... have the T-Shirt! you are a gentleman and a scholar. but, my point was about the state of the system as observed (perceived) from outside the machine, within the business domain model, where (and this is exactly the point I am trying to make), null does mean unknown.Quodlibet
U
98

There was a huge debate about this oddity when the feature was originally designed back in C# 2.0. The problem is that C# users are completely used to this being meaningful:

if(someReference == null)

When extending equality to nullable value types, you have the following choices.

  1. Nullable equality is truly lifted. If one or both of the operands is null then the result is neither true, nor false, but null. In this case you can either:

    • a) Make it illegal to have a nullable value type equality in an if statement, because the if statement needs a bool, not a nullable bool. Instead, require everyone to use HasValue if they want to compare to null. This is verbose and irritating.

    • b) Automatically convert null to false. The downside of this is that x==null returns false if x is null, which is confusing and works against people's understanding of null comparisons with reference types.

  2. Nullable equality is not lifted. Nullable equality is either true or false, and comparison to null is a null check. This makes nullable equality inconsistent with nullable inequality.

None of these choices is obviously correct; they all have pros and cons. VBScript chooses 1b, for example. After much debate the C# design team chose #2.

Unblock answered 9/12, 2010 at 15:56 Comment(18)
How is nullable equality inconsistent with nullable inequality in choice #2?Avuncular
@MCS: In exactly the way that motivates the question in the first place. == can be true when <= is false.Unblock
@Eric: Thanks - I thought 'inequality' referred only to != which actually is consistent with ==. Didn't realize it's a mathematical term: en.wikipedia.org/wiki/Inequality_(mathematics).Avuncular
Well, the other issue (which you haven't addressed) is what to do when you try to do <, <=, =>, or > when one of the operands is null. In C#, the answer is return false. In the Scala/Java String class, the answer is to throw a NullPointerException.Filtrate
How about lifting equality of nullable types, and forbidding the use of a nullable type in an "if" statement, but providing that a null constant is not a "nullable type"?Anastos
@supercat: A null literal is not of nullable type; it is of no type but implicitly convertible to a nullable type. But nit-picking aside, sure, you could do that. However that makes "x == null" and "a = null; ... x == a" have different semantics, which seems weird.Unblock
@Eric Lippert: I can see that having comparison operators between nullable types return a nullable boolean could be confusing. What would have been the effect of forbidding such operators? If x and a are nullable types, x==null would be an acceptable shorthand to test if x is currently null, and a==null would test if b is currently null, but (x==a) would be forbidden. A nullable ValueType may be either a null type or a ValueType; other rules in the language allow anything to be compared to a literal null, but otherwise does not allow different types to be compared.Anastos
@Eric Lippert: Incidentally, at least in vb.net, the "=" operator isn't always transitive when comparing different types. Two variables of type 'long' may compare different from each other, and yet both match a variable of type 'double'.Anastos
@supercat: The equality operator is intransitive in C# as well in some scenarios involving change of types. Clearly equality is not an equivalence relation across the entire possible set of values in C#, though there are subsets of the type system in which it is an equivalence relation. See blogs.msdn.com/b/ericlippert/archive/2009/09/28/… for more thoughts on this subject.Unblock
Another choice: If you a >=, <=, >, or < operator and one or both operands is null, throw a NullReferenceException . This places a burden on the programmer similar to 1a. I consider this and 1a to be ivory tower approaches: they feel more "correct," but they ignore the practical consideration that people have to actually use them.Tackett
@Brian: then why allow the operators on nullable types at all? If they always throw on the null value of the nullable type then you might as well just define the operator only on the non-nullable types and make the user insert the conversion to non-nullable, since that's what they're going to have to do to eliminate the exception.Unblock
Accepted this answer for pointing out the history and reasoning. It's a C# question after all...Pamper
@Eric: That's why I compared that solution to 1a. The advantage of the NullReferenceException solution is that you save typing if you "know" that they are not null. Though why you would "know" that but still be using a nullable is a mystery. Anyhow, I acknowledge that this is a bad idea; it's kind of like 1a except if you try to treat nullable as a regular number it will wait until runtime to inform you of your error instead of compile time.Tackett
">=" should be equivalent with "> || ==" that way rule number 2 is still kept and both ">=" and "==" return the same value. Because of the strange nullable equality check two expressions which are equivalent in all other contexts "> || ==" and ">=" are not equivalent in the case of nullabe equality checks. This has great effects on the composability of expressions, where this special case must be accounted for.Clint
after all >= logically is an operator composed of three basic operators: greater than, boolean or, equalsClint
Users may be used to if(someReference == null) being allowed, but it shouldn't be. It might be a convenient fudge, but then leads to problems like this resulting in nonsense answers that are internally contradictory. This seems to be the antithesis of the Pit Of Success.Giacobo
Did you mean to write "VB.NET" instead of "VBScript"?Hukill
@Heinzi: No, I wrote what I intended to write.Unblock
F
60

Because Equality is defined separately from Comparability.
You can test x == null but x > null is meaningless. In C# it will always be false.

Firebox answered 9/12, 2010 at 15:40 Comment(3)
+1: here's the MSDN link msdn.microsoft.com/en-us/library/2cf62fcy.aspx, but unfortunately, they forgot to explain the comparison operators behavior in case of 2 nulls (they mentioned only equality)...Brickbat
yes, but the operator is greater than or equal. I see the truth table, but I would tend to agree with the OP, >= is greater or equal, if null==null is true, null>=null should also be true. I guess we just chalk it up to implementation and user convenience to preserve the ==null checks.Hodgepodge
@David, see Eric's answer, "None of these choices is obviously correct". But in general, when a type is Equatable but not Comparable then >= is simply not defined.Firebox
A
12

Another way of describing '>=' is: Not Less Than. No mention of equals. As soon as one of the operands in a non-equality test is Null, the result is unknown as well (is null). However if you want to know if both operands are Null, then Null == Null is a reasonable test (should result in true). Getting rid of the inequality part of the operator makes all the difference.

The following code example from http://msdn.microsoft.com/en-us/library/2cf62fcy.aspx#sectionToggle4 summarizes how C# treats Null:

int? num1 = 10;   
int? num2 = null;   
if (num1 >= num2)   
{   
    Console.WriteLine("num1 is greater than or equal to num2");   
}   
else   
{   
    // This clause is selected, but num1 is not less than num2.   
    Console.WriteLine("num1 >= num2 returned false (but num1 < num2 also is false)");   
}   

if (num1 < num2)   
{   
    Console.WriteLine("num1 is less than num2");   
}   
else   
{   
    // The else clause is selected again, but num1 is not greater than   
    // or equal to num2.   
    Console.WriteLine("num1 < num2 returned false (but num1 >= num2 also is false)");   
}   

if (num1 != num2)   
{   
    // This comparison is true, num1 and num2 are not equal.   
    Console.WriteLine("Finally, num1 != num2 returns true!");   
}   

// Change the value of num1, so that both num1 and num2 are null.   
num1 = null;   
if (num1 == num2)   
{   
    // The equality comparison returns true when both operands are null.   
    Console.WriteLine("num1 == num2 returns true when the value of each is null");   
}   

/* Output:   
 * num1 >= num2 returned false (but num1 < num2 also is false)   
 * num1 < num2 returned false (but num1 >= num2 also is false)   
 * Finally, num1 != num2 returns true!   
 * num1 == num2 returns true when the value of each is null   
 */   
Antipathy answered 9/12, 2010 at 15:41 Comment(3)
That's an interesting mental model. However Section §1.4 of the C# Spec call these operators Less Than Or Equal and Greater than or equalRegicide
@Conrad Which just illustrates the problems of translating a programming language (C# in this case) into english. IMHO, whenever Nulls figure into logic, you need to deal with a tri-state result (true, false, unknown). Any expression involving Null should result in unknown with the one exception of Null == x which is an explicit test for unknown resulting in either true or false.Antipathy
@NealB: Actually, the spec states that >= and <= mean what you'd expect them to mean- section §7.10 makes it clear that the operation 'x op y' for <= and >= is meant to be equal-to or greater-than/less-than, as one would expect.Giacobo
R
2

>= operates on a numeric value; which null is not.

You could overload the >= operator to provide what you desire on a specific type.

Raper answered 9/12, 2010 at 15:40 Comment(3)
it does operate on null types, it returns falseHodgepodge
It handles null types yes...semantics on what we are going to define as "operates". Defensive coding; is it null then do x versus treating null as a literal value when making decisions during the evaluation.Raper
You cannot usually overload operators because you can only define them inside their own classes. So in this case you would need access to Nullable<T>'s code.Opportunist
H
0

What values would you expect?

null == null true

null >= null false

null > null false

null <= null false

null < null false

null != null false

1 == null false

1 >= null false

1 > null false

1 <= null false

1 < null false

1 != null true aka !(1 == null)

Hypoxia answered 9/12, 2010 at 17:10 Comment(0)
C
0

>= only means "greater than or equal" when used in that specific well defined way. When used on a class with overloaded operators it means anything the class developer wants it to mean. When applied to a string-like class, it might mean "sorts the same or higher" or it might mean "the same length or longer".

Cordeiro answered 9/12, 2010 at 23:18 Comment(0)
I
0

Since by default an int cannot be null and its value will be set to 0, the operator of > and < which is built for int type, expects to work with values and not with nulls.

see my answer to a similar question where I wrote some ways to handle nullable int with the less < and greater > operators https://mcmap.net/q/266175/-c-do-you-need-to-check-if-something-has-a-value-and-if-something-is-greater-than-0

Inebriety answered 24/7, 2018 at 21:27 Comment(0)
H
-1

NULL is not zero (numeric or binary value), a zero-length string, or blank (character value). So any comparison operator will always return false on it. Read more about it here

Homage answered 9/12, 2010 at 15:35 Comment(3)
Database NULL is not C# null. Furthermore, comparison operators on C# nullable types are a strange beast that do not necessarily follow the usual rules for null comparisons.Bollinger
The answer is still right, just the link is wrong. msdn.microsoft.com/en-us/library/2cf62fcy.aspx#sectionToggle4Halflength
@unholy: The answer is wrong, and more importantly, it's based on wrong reasoning.Bollinger

© 2022 - 2024 — McMap. All rights reserved.