在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");
}
为什么会有区别?
答案 0 :(得分:87)
VB.NET和C#.NET是不同的语言,由不同的团队构建,他们对使用做出了不同的假设;在这种情况下,NULL比较的语义。
我的个人偏好是VB.NET语义,它本质上给NULL语义“我还不知道”。然后比较5到“我还不知道”。自然是“我还不知道”;即NULL。这具有在(大多数(如果不是全部)SQL数据库中镜像NULL行为的额外优点。正如here所解释的那样,这也是对三值逻辑的一种更为标准(比C#)的解释。
C#团队对NULL的含义做出了不同的假设,导致您显示的行为差异。 Eric Lippert wrote a blog about the meaning of NULL in C#。 Per Eric Lippert:“我还在VB / VBScript和JScript here和here中写过关于空值的语义。”
在任何可能有NULL值的环境中,重要的是要认识到排除中间的定律(即A或〜A在同义上是真的)不再可以依赖。
<强>更新强>
bool
(而不是bool?
)只能取值TRUE和FALSE。但是,NULL的语言实现必须决定NULL如何通过表达式传播。在VB中,表达式5=null
和5<>null
两者都返回false。在C#中,可比表达式5==null
和5!=null
只有第二第一个 [更新2014-03-02 - PG] 返回false。但是,在任何支持null的环境中,程序员有责任知道该语言使用的真值表和空传播。
答案 1 :(得分:36)
因为x <> y
返回Nothing
而不是true
。由于未定义x
,因此未定义它。 (类似于SQL null)。
注意:VB.NET Nothing
&lt;&gt; 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 language specification:
7.1.1可空值类型 ...可空值类型可以包含与非可空值相同的值 类型的版本以及null值。因此,对于一个可以为空的 值类型,为类型的变量赋值Nothing设置值 变量的值为空值,而不是值的零值 类型。
例如:
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) '
答案 2 :(得分:17)
查看生成的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&lt; bool&gt; (不是bool,虚假或真实!)。未定义转换为bool是错误的。
Nothing
与总是Nothing
的内容进行比较,而不是在Visual Basic中为false(与SQL中的相同)。
答案 3 :(得分:6)
这里观察到的问题是一个更普遍的问题的特殊情况,即在至少某些情况下可能有用的不同的相等定义的数量超过了表达它们的常用方法的数量。在某些情况下,这个问题由于一个令人遗憾的信念而变得更加糟糕,即不同的方法来测试平等会产生不同的结果,并且可以通过让不同形式的平等产生尽可能相同的结果来避免这种混淆。
实际上,混淆的根本原因是错误的信念,即不同形式的平等和不平等测试应该产生相同的结果,尽管不同的语义在不同的情况下是有用的。例如,从算术的角度来看,能够使Decimal
仅在尾随零的数量上相差较大是有用的。同样,double
值为正零和负零。另一方面,从缓存或实习的角度来看,这种语义可能是致命的。例如,假设有一个Dictionary<Decimal, String>
,myDict[someDecimal]
应该等于someDecimal.ToString()
。如果一个人有许多Decimal
个值想要转换为字符串并且期望有许多重复项,那么这样的对象似乎是合理的。不幸的是,如果使用这种缓存来转换12.3米和12.40米,接着是12.30米和12.4米,后者的值将产生“12.3”,而“12.40”而不是“12.30”和“12.4”。
回到手头的问题,有多种明智的方法来比较可空对象的平等性。 C#认为其==
运算符应该反映Equals
的行为。 VB.NET认为其行为应与其他语言的行为相同,因为任何想要Equals
行为的人都可以使用Equals
。在某种意义上,正确的解决方案是拥有一个三向“if”结构,并要求如果条件表达式返回三值结果,则代码必须指定null
情况下应该发生的情况。由于这不是语言的选择,下一个最好的选择是简单地学习不同的语言如何工作并认识到它们不相同。
顺便提一下,Visual Basic的“Is”运算符(缺少C)可用于测试可空对象实际上是否为空。虽然可以合理地质疑if
测试是否应该接受Boolean?
,但在可空类型上调用时,让正常比较运算符返回Boolean?
而不是Boolean
是一个有用的功能。顺便说一下,在VB.NET中,如果一个人试图使用等于运算符而不是Is
,那么就会得到一个警告:比较的结果总是Nothing
,并且应该使用{{1}如果有人想测试某些东西是否为空。
答案 4 :(得分:3)
如果我没记错的话,VB中的'Nothing'意味着“默认值”。对于值类型,对于引用类型,这是null的默认值。因此,不给结构赋予任何东西,这根本不是问题。
答案 5 :(得分:2)
这是VB的一个明确的怪异。
在VB中,如果要比较两种可空类型,则应使用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
答案 6 :(得分:0)
您的VB代码完全不正确 - 如果您将“x&lt;&gt; y”更改为“x = y”,则结果仍然会显示“false”。对于可为空的实例,最常见的表达方式是“Not x.Equals(y)”,这将产生与C#中“x!= y”相同的行为。