为什么在检查null和VB.NET和C#中的值方面有所不同?

在VB.NET中发生这种情况:

Dim x As System.Nullable(Of Decimal) = Nothing Dim y As System.Nullable(Of Decimal) = Nothing y = 5 If x <> y Then Console.WriteLine("true") Else Console.WriteLine("false") '' <-- I got this. Why? End If 

但在C#中发生这种情况:

 decimal? x = default(decimal?); decimal? y = default(decimal?); y = 5; if (x != y) { Debug.WriteLine("true"); // <-- I got this -- I'm with you, C# :) } else { Debug.WriteLine("false"); } 

为什么有差异?

VB.NET和C#.NET是不同的语言,由不同的团队谁使用不同的假设build立了不同的语言, 在这种情况下是NULL比较的语义。

我个人的偏好是对于VB.NET语义,它本质上为NULL提供了“我还不知道”的语义。 然后比较5到“我还不知道”。 自然是“我还不知道”; 即NULL。 这具有在SQL数据库中(大部分,如果不是全部)镜像NULL的行为的附加优点。 这也是一个比三值逻辑更为标准的(比C#的)解释,如这里所解释的。

C#团队对NULL的含义做出了不同的假设,导致了您所显示的行为差异。 Eric Lippert在C#中写了一篇关于NULL含义的博客 。 Per Eric Lippert:“我还在这里和这里写了关于VB / VBScript和JScript中空值的语义。

在任何可能出现NULL值的环境中,重要的是认识到排除中的定律(即A或〜A是同义的)是不能被依赖的。

更新:

一个bool (而不是bool? )只能取值TRUE和FALSE。 但是,NULL的语言实现必须决定NULL如何通过expression式传播。 在VB中,expression式5=null5<>null BOTH返回false。 在C#中,可比较的expression式5==null5!=null只有第二个 [updated 2014-03-02 – PG]返回false。 但是,在任何支持null的环境中,程序员都有责任知道该语言使用的真值表和空传播。

因为x <> y返回Nothing而不是true 。 它没有被定义,因为x没有被定义。 (类似于SQL null)。

注意:VB.NET Nothing <> C# null

只有当它有一个值时,你也必须比较Nullable(Of Decimal)的值。

所以上面的VB.NET比较类似这个(看起来不太正确):

 If x.HasValue AndAlso y.HasValue AndAlso x <> y Then Console.WriteLine("true") Else Console.WriteLine("false") End If 

VB.NET 语言规范 :

7.1.1可为 null的值types可为null的值types可以包含与该types的非可为空版本相同的值以及空值。 因此,对于可为null的值types,将Nothing分配给types的variables将variables的值设置为空值,而不是值types的零值。

例如:

 Dim x As Integer = Nothing Dim y As Integer? = Nothing Console.WriteLine(x) ' Prints zero ' Console.WriteLine(y) ' Prints nothing (because the value of y is the null value) ' 

看看生成的CIL (我已经转换为C#):

C#:

 private static void Main(string[] args) { decimal? x = null; decimal? y = null; y = 5M; decimal? CS$0$0000 = x; decimal? CS$0$0001 = y; if ((CS$0$0000.GetValueOrDefault() != CS$0$0001.GetValueOrDefault()) || (CS$0$0000.HasValue != CS$0$0001.HasValue)) { Console.WriteLine("true"); } else { Console.WriteLine("false"); } } 

Visual Basic:

 [STAThread] public static void Main() { decimal? x = null; decimal? y = null; y = 5M; bool? VB$LW$t_struct$S3 = new bool?(decimal.Compare(x.GetValueOrDefault(), y.GetValueOrDefault()) != 0); bool? VB$LW$t_struct$S1 = (x.HasValue & y.HasValue) ? VB$LW$t_struct$S3 : null; if (VB$LW$t_struct$S1.GetValueOrDefault()) { Console.WriteLine("true"); } else { Console.WriteLine("false"); } } 

您将看到Visual Basic中的比较返回Nullable <bool>(不bool,false或true!)。 而未定义转换为布尔是假的。

Nothing东西总是与Nothing相比,在Visual Basic中不是false(与SQL中的相同)。

这里所观察到的问题是一个更普遍问题的特殊情况,即在至less某些情况下可能有用的不同等式定义的数量超过了expression它们的常用手段的数量。 这个问题在某些情况下会因为一个不幸的信念而变得更糟,因为用不同的平等检验手段产生不同的结果是混淆不清的,只要有可能,不同forms的平等就会产生相同的结果。

实际上,造成混淆的根本原因是错误的信念,认为不同forms的平等和不平等testing应该产生相同的结果,尽pipe不同的语义在不同的情况下是有用的。 例如,从算术的angular度来看,能够使只有拖尾零的数量相差相等的Decimal数才是有用的。 同样,对于正值为零和负值为零的double值也是如此。 另一方面,从caching或实习angular度来看,这样的语义可能是致命的。 例如,假设有一个Dictionary<Decimal, String> ,使得myDict[someDecimal]应该等于someDecimal.ToString() 。 这样的对象看起来是合理的,如果有一个人想要转换为string,并期望有很多重复的Decimal值。 不幸的是,如果使用这种高速caching来转换12.3米和12.40米,其次是12.30米和12.4米,后面的值将产生“12.3”,而“12.40”而不是“12.30”和“12.4”。

回到现在的事情,比较可空对象的平等是不止一个明智的方法。 C#的立场是它的==运算符应该反映Equals的行为。 VB.NET的立场是它的行为应该反映一些其他语言,因为任何想要Equals行为的人都可以使用Equals 。 从某种意义上说,正确的解决scheme应该是具有三向“if”构造,并且要求如果条件expression式返回三值结果,则代码必须指定在null情况下应该发生什么。 由于这不是一种语言的select,所以下一个最好的select是简单地学习不同的语言是如何工作的,并认识到它们是不一样的。

顺便提一下,Visual Basic的“Is”运算符在C中缺less,可用于testing可空对象实际上是否为null。 虽然有人可能有理由怀疑iftesting是否应该接受Boolean? ,有正常的比较运算符返回Boolean? 当调用可为空types时,而不是Boolean是一个有用的function。 顺便说一句,在VB.NET中,如果试图使用等号运算符而不是Is ,那么会得到一个警告,即比较的结果将始终为Nothing ,如果要testing是否为null,则应该使用Is

可能是这个post很好的帮助你:

如果我没有记错的话,VB中的“Nothing”意味着“默认值”。 对于值types,这是引用types的默认值,它将为null。 因此,对结构分配什么都不是问题。

这是一个确定的VB的怪异。

在VB中,如果你想比较两个可空types,你应该使用Nullable.Equals()

在你的例子中,它应该是:

 Dim x As System.Nullable(Of Decimal) = Nothing Dim y As System.Nullable(Of Decimal) = Nothing y = 5 If Not Nullable.Equals(x, y) Then Console.WriteLine("true") Else Console.WriteLine("false") End If 

你的VB代码是不正确的 – 如果你把“x <> y”改成“x = y”,你的结果仍然是“false”。 对于可为null的实例,最常见的expression式是“Not x.Equals(y)”,这将产生与C#中的“x!= y”相同的行为。