为什么在VB.NET和C#中检查null值是否存在差异?

bli*_*eis 108 c# vb.net null .net-4.0

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
Run Code Online (Sandbox Code Playgroud)

但在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");
}
Run Code Online (Sandbox Code Playgroud)

为什么会有区别?

Pie*_*ens 86

VB.NET和C#.NET是不同的语言,由不同的团队构建,他们对使用做出了不同的假设; 在这种情况下,NULL比较的语义.

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

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

在任何可能有NULL值的环境中,重要的是要认识到不再可以依赖排除中间的定律(即A或〜A在同义上是真的).

更新:

A bool(与a相对bool?)只能取值TRUE和FALSE.但是,NULL的语言实现必须决定NULL如何通过表达式传播.在VB中,表达式5=null5<>nullBOTH返回false.在C#,可比表达式5==null5!=null第二第一[更新2014年3月2日- PG]返回false.但是,在任何支持null的环境中,程序员有责任知道该语言使用的真值表和空传播.

  • 而且仅供参考,以这种方式使C#与VB不兼容的决定是一个有争议的决定.我当时不在语言设计团队,但是这个决定的讨论数量相当可观. (27认同)
  • 谢谢你的链接.我还在这里写了关于VB/VBScript和JScript中空值的语义:http://blogs.msdn.com/b/ericlippert/archive/2003/09/30/53120.aspx和这里:http:// blogs. msdn.com/b/ericlippert/archive/2003/10/01/53128.aspx (4认同)
  • @ BlueRaja-DannyPflughoeft在C#`bool`中不能有3个值,只有两个.这是"bool?",可以有三个值.无论操作数的类型如何,`operator ==`和`operator!=`都返回`bool`,而不是`bool?`.另外,`if`语句只能接受`bool`,而不是`bool?`. (2认同)

Tim*_*ter 36

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

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

Nullable(Of Decimal)如果它具有值,您还必须比较a的值.

所以上面的VB.NET与此类似(看起来不太正确):

If x.HasValue AndAlso y.HasValue AndAlso x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false")  
End If
Run Code Online (Sandbox Code Playgroud)

VB.NET 语言规范:

7.1.1可空值类型 ...可空值类型可以包含与该类型的非可空版本以及空值相同的值.因此,对于可空值类型,将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) '
Run Code Online (Sandbox Code Playgroud)

  • "VB.NET Nothing <> C#null"对于C#,它返回true,对VB.Net是否为false?开个玩笑:-p (16认同)

not*_*row 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

您将看到Visual Basic中的比较返回Nullable <bool>(不是bool,false或true!).未定义转换为bool是错误的.

Nothing与任何总是相比Nothing,在Visual Basic中不是假的(它与SQL中的相同).

  • @DavidHeffernan,因为这显示了非常明确的语言差异. (3认同)
  • @Yossarian你认为语言规范在这个问题上是模棱两可的.我不同意.IL是一个可以改变的实现细节; 规格不是. (2认同)
  • @DavidHeffernan:我喜欢你的态度并鼓励你去尝试.VB语言规范有时很难解析.Lucian已经改进了几年了,但是仍然很难确定这些角落案件的确切含义.我建议您获取规范的副本,进行一些研究,并报告您的发现. (2认同)
  • @Yossarian执行您提供的IL代码的结果不会有任何变化,但提供的C#/ VB代码将被编译成您显示的IL代码**可能会发生变化(只要其行为有所改变) IL也符合语言规范的定义. (2认同)

sup*_*cat 6

这里观察到的问题是一个更普遍的问题的特殊情况,即在至少某些情况下可能有用的不同的相等定义的数量超过了表达它们的常用方法的数量.在某些情况下,这个问题由于一个令人遗憾的信念而变得更加糟糕,即用不同的方法测试平等会产生不同的结果,并且可以通过让不同形式的平等在任何可能的情况下产生相同的结果来避免这种混淆.

实际上,混淆的根本原因是错误的信念,即不同形式的平等和不平等测试应该产生相同的结果,尽管不同的语义在不同的情况下是有用的.例如,从算术的角度来看,能够使Decimal只有尾随零的数量不同的数据相等是有用的.同样,对于double正零和负零等值.另一方面,从缓存或实习的角度来看,这种语义可能是致命的.举个例子,一个有一个Dictionary<Decimal, String>这样myDict[someDecimal]应该等于someDecimal.ToString().如果有Decimal一个人想要转换为字符串并希望有许多重复项,那么这样的对象似乎是合理的.不幸的是,如果使用这种高速缓存转换12.3 m和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测试是否应该接受a Boolean?,但是正常比较运算符返回Boolean?而不是Boolean在可空类型上调用时是一个有用的特性.顺便说一下,在VB.NET中,如果一个人试图使用相等运算符而不是Is,那么就会得到一个警告:比较的结果总是如此Nothing,并且Is如果有人想测试是否为空,则应该使用.