Comparing Nullable values produces some counter-intuitive results, and its behavior is language-dependent. category 'KB', language C# and VB.NET, created 13-Feb-2010, version V1.0, by Luc Pattyn |
License: The author hereby grants you a worldwide, non-exclusive license to use and redistribute the files and the source code in the article in any way you see fit, provided you keep the copyright notice in place; when code modifications are applied, the notice must reflect that. The author retains copyright to the article, you may not republish or otherwise make available the article, in whole or in part, without the prior written consent of the author. Disclaimer: This work is provided “as is”, without any express or implied warranties or conditions or guarantees. You, the user, assume all risk in its use. In no event will the author be liable to you on any legal theory for any special, incidental, consequential, punitive or exemplary damages arising out of this license or the use of the work or otherwise. |
Since version 2.0 the .NET Framework offers nullable types; when a value type is nullable, it allows for a null value, which may come in handy as it can indicate the absence of an actual value. However, there are some strange things going on when comparing nullable values, and the exact behavior is language-dependent.
I created basically the same console application both in C# and in VB.NET; here is the C# code:
using System;
namespace Nullable1 {
class Program {
static void Main(string[] args) {
int? x = null;
int? y = null;
Compare(x, y);
x = 1;
Compare(x, y);
y = 2;
Compare(x, y);
x = null;
Compare(x, y);
Console.ReadLine();
}
private static void Compare(int? x, int? y) {
if (x == y) Console.WriteLine("{0} == {1} is True", toString(x), toString(y));
else Console.WriteLine("{0} == {1} is False", toString(x), toString(y));
if (x != y) Console.WriteLine("{0} != {1} is True", toString(x), toString(y));
else Console.WriteLine("{0} != {1} is False", toString(x), toString(y));
if (x >= y) Console.WriteLine("{0} >= {1} is True", toString(x), toString(y));
else Console.WriteLine("{0} >= {1} is False", toString(x), toString(y));
if (x > y) Console.WriteLine("{0} > {1} is True", toString(x), toString(y));
else Console.WriteLine("{0} > {1} is False", toString(x), toString(y));
if (x < y) Console.WriteLine("{0} < {1} is True", toString(x), toString(y));
else Console.WriteLine("{0} < {1} is False", toString(x), toString(y));
if (x <= y) Console.WriteLine("{0} <= {1} is True", toString(x), toString(y));
else Console.WriteLine("{0} <= {1} is False", toString(x), toString(y));
Console.WriteLine("Nullable.Equals({0}, {1}) is {2}", toString(x), toString(y), Nullable.Equals(x, y));
Console.WriteLine("Nullable.Compare({0}, {1}) is {2}", toString(x), toString(y), Nullable.Compare(x, y));
Console.WriteLine();
}
private static string toString(int? x) {
if (x.HasValue) return x.ToString();
return "null";
}
}
}
And this is the VB.NET code that at first glance is equivalent (but later on we will see the results are different):
Module Nullable1
Sub Main()
Dim x As Nullable(Of Int32) = Nothing
Dim y As Nullable(Of Int32) = Nothing
Compare(x, y)
x = 1
Compare(x, y)
y = 2
Compare(x, y)
x = Nothing
Compare(x, y)
Console.ReadLine()
End Sub
Private Sub Compare(ByVal x As Nullable(Of Int32), ByVal y As Nullable(Of Int32))
If x = y Then
Console.WriteLine("{0} = {1} is True", toString(x), toString(y))
Else
Console.WriteLine("{0} = {1} is False", toString(x), toString(y))
End If
If x <> y Then
Console.WriteLine("{0} <> {1} is True", toString(x), toString(y))
Else
Console.WriteLine("{0} <> {1} is False", toString(x), toString(y))
End If
If x >= y Then
Console.WriteLine("{0} >= {1} is True", toString(x), toString(y))
Else
Console.WriteLine("{0} >= {1} is False", toString(x), toString(y))
End If
If x > y Then
Console.WriteLine("{0} > {1} is True", toString(x), toString(y))
Else
Console.WriteLine("{0} > {1} is False", toString(x), toString(y))
End If
If x < y Then
Console.WriteLine("{0} < {1} is True", toString(x), toString(y))
Else
Console.WriteLine("{0} < {1} is False", toString(x), toString(y))
End If
If x <= y Then
Console.WriteLine("{0} <= {1} is True", toString(x), toString(y))
Else
Console.WriteLine("{0} <= {1} is False", toString(x), toString(y))
End If
Console.WriteLine("Nullable.Equals({0}, {1}) is {2}", toString(x), toString(y), Nullable.Equals(x, y))
Console.WriteLine("Nullable.Compare({0}, {1}) is {2}", toString(x), toString(y), Nullable.Compare(x, y))
Console.WriteLine()
End Sub
Private Function toString(ByVal x As Nullable(Of Int32)) As String
If x.HasValue Then Return x.ToString()
Return "Nothing"
End Function
End Module
And here is the output for each of those applications using Visual Studio 2008; targetting either .NET 2.0 or 3.5 returns the same results:
null == null is True
null != null is False
null >= null is False
null > null is False
null < null is False
null <= null is False
Nullable.Equals(null, null) is True
Nullable.Compare(null, null) is 0
1 == null is False
1 != null is True
1 >= null is False
1 > null is False
1 < null is False
1 <= null is False
Nullable.Equals(1, null) is False
Nullable.Compare(1, null) is 1
1 == 2 is False
1 != 2 is True
1 >= 2 is False
1 > 2 is False
1 < 2 is True
1 <= 2 is True
Nullable.Equals(1, 2) is False
Nullable.Compare(1, 2) is -1
null == 2 is False
null != 2 is True
null >= 2 is False
null > 2 is False
null < 2 is False
null <= 2 is False
Nullable.Equals(null, 2) is False
Nullable.Compare(null, 2) is -1
|
Nothing = Nothing is False
Nothing <> Nothing is False
Nothing >= Nothing is False
Nothing > Nothing is False
Nothing < Nothing is False
Nothing <= Nothing is False
Nullable.Equals(Nothing, Nothing) is True
Nullable.Compare(Nothing, Nothing) is 0
1 = Nothing is False
1 <> Nothing is False
1 >= Nothing is False
1 > Nothing is False
1 < Nothing is False
1 <= Nothing is False
Nullable.Equals(1, Nothing) is False
Nullable.Compare(1, Nothing) is 1
1 = 2 is False
1 <> 2 is True
1 >= 2 is False
1 > 2 is False
1 < 2 is True
1 <= 2 is True
Nullable.Equals(1, 2) is False
Nullable.Compare(1, 2) is -1
Nothing = 2 is False
Nothing <> 2 is False
Nothing >= 2 is False
Nothing > 2 is False
Nothing < 2 is False
Nothing <= 2 is False
Nullable.Equals(Nothing, 2) is False
Nullable.Compare(Nothing, 2) is -1
|
The results are normal for the Equals
and Compare
methods.
The results are normal too for comparison operators when both operands are not null/Nothing;
however when one or both operands are null or Nothing:
==
and !=
work in the intuitive way.<
and >=
)
don't always produce opposite results, and the OR logic in <=
and >=
seems defective
as they can return false even when ==
returns true!Nullable comparisons hold a couple of surprises, the results may be counter-intuitive and they depend on the language used.
Perceler |
Copyright © 2012, Luc Pattyn |
Last Modified 02-Sep-2013 |