【问题标题】:Why is there a difference in checking null against a value in VB.NET and C#?为什么在 VB.NET 和 C# 中检查 null 值会有所不同?
【发布时间】:2013-03-20 12:50:39
【问题描述】:

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");
}

为什么会有差异?

【问题讨论】:

  • 太可怕了。
  • 我相信 default(decimal?) 返回 0,而不是 null
  • @RyanFrame NO.由于它是可空类型,它返回null
  • 哦,是的......对......在VB中If条件不需要评估为布尔值...... uuuugh编辑:所以Nothing &lt;&gt; Anything = Nothing结果在If 中采取否定/其他路线。
  • @JMK:Null、Nothing 和 Empty 实际上都有细微的不同。 如果它们都一样,那么你就不需要三个了。

标签: c# vb.net .net-4.0 null


【解决方案1】:

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

我个人偏爱 VB.NET 语义,它本质上赋予 NULL 语义“我还不知道”。然后将 5 与“我还不知道”进行比较。自然是“我还不知道”;即NULL。这具有在(大多数如果不是全部)SQL 数据库中镜像 NULL 行为的额外优势。这也是对三值逻辑的更标准(比 C# 的)解释,如 here 所述。

C# 团队对 NULL 的含义做出了不同的假设,从而导致您表现出的行为差异。 Eric Lippert wrote a blog about the meaning of NULL in C#。 Per Eric Lippert:“我还写了关于 VB / VBScript 和 JScript herehere 中空值语义的文章。

在任何可能出现 NULL 值的环境中,重要的是要认识到排中定律(即 A 或 ~A 在重言式上为真)不再可以依赖。

更新:

bool(相对于bool?)只能采用值 TRUE 和 FALSE。然而,NULL 的语言实现必须决定 NULL 如何通过表达式传播。在 VB 中,表达式 5=null5&lt;&gt;null 都返回 false。在 C# 中,在可比较的表达式 5==null5!=null 中,只有 second 第一个 [updated 2014-03-02 - PG] 返回 false。但是,在任何支持 null 的环境中,程序员都有责任了解该语言使用的真值表和 null 传播。

更新

Eric Lippert 关于语义的博客文章(在下面的 cmets 中提到)现在位于:

【讨论】:

  • 感谢您的链接。我还在这里写了关于 VB / VBScript 和 JScript 中空值的语义:blogs.msdn.com/b/ericlippert/archive/2003/09/30/53120.aspx 和这里:blogs.msdn.com/b/ericlippert/archive/2003/10/01/53128.aspx
  • 而且仅供参考,以这种方式使 C# 与 VB 不兼容的决定是一个有争议的决定。当时我不在语言设计团队,但做出这个决定的争论相当多。
  • @BlueRaja-DannyPflughoeft 在 C# 中 bool 不能有 3 个值,只有两个。 bool? 可以有三个值。无论操作数的类型如何,operator ==operator != 都返回 bool,而不是 bool?。此外,if 语句只能接受 bool,不能接受 bool?
  • 在 C# 中,表达式 5=null5&lt;&gt;null 无效。在5 == null5 != null 中,你确定是第二个返回false
  • @BenVoigt:谢谢。所有这些赞成票,你是第一个发现那个错字的人。 ;-)
【解决方案2】:

因为x &lt;&gt; 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 language specification:

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) '

【讨论】:

  • "VB.NET Nothing C# null" 它对 C# 返回 true,对 VB.Net 返回 false?开个玩笑:-p
【解决方案3】:

查看生成的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、false 或 true!)。并且 undefined 转换为 bool 是 false。

Nothing 与始终为 Nothing 的值相比,在 Visual Basic 中不为假(与 SQL 中相同)。

【讨论】:

  • 为什么要通过反复试验来回答这个问题?应该可以从语言规范中做到这一点。
  • @DavidHeffernan,因为这表明语言的差异非常明确。
  • @Yossarian 你认为语言规范在这个问题上是模棱两可的。我不同意。 IL 是一个可能发生变化的实现细节;规格不是。
  • @DavidHeffernan:我喜欢你的态度并鼓励你尝试。 VB 语言规范有时难以解析。 Lucian 多年来一直在改进它,但仍然很难弄清楚这些极端情况的确切含义。我建议您获取一份规范副本,进行一些研究,然后报告您的发现。
  • @Yossarian 你提供的IL代码的执行结果不会改变,但是提供的C#/VB代码会被编译成你展示的IL代码是 i> 可能会发生变化(只要该 IL 的行为也符合语言规范的定义)。
【解决方案4】:

这里观察到的问题是一个更一般问题的特殊情况,即至少在某些情况下可能有用的不同相等定义的数量超过了表达它们的常用方法的数量。在某些情况下,这个问题会因为一种不幸的信念而变得更糟,即使用不同的相等性测试方法会产生不同的结果会造成混淆,而这种混淆可以通过尽可能让不同形式的相等性产生相同的结果来避免。

实际上,混淆的根本原因是一种错误的信念,即认为不同形式的相等和不等式测试应该会产生相同的结果,尽管事实上不同的语义在不同的情况下是有用的。例如,从算术的角度来看,能够让Decimal 仅在尾随零的数量上比较相等是很有用的。同样对于double 值,如正零和负零。另一方面,从缓存或实习的角度来看,这种语义可能是致命的。例如,假设有一个Dictionary&lt;Decimal, String&gt;,这样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,如果想要使用,则应使用 Is测试是否为空。

【讨论】:

  • 在 C# 中测试一个类是否为空是由== null 完成的。并且测试可空值类型是否有值由.hasValue 完成。 Is Nothing 运算符有什么用? C# 确实有is,但它会测试类型兼容性。鉴于这些,我真的不确定你的最后一段想要说什么。
  • @ErikE:vb.net 和 C# 都允许使用与 null 的比较来检查可空类型的值,尽管两种语言都将其视为 HasValue 检查的语法糖,在至少在类型已知的情况下(我不确定为泛型生成了什么代码)。
  • 在泛型中,您可能会遇到关于可空类型和重载决议的棘手问题...
【解决方案5】:

可能是 this 贴好帮助你:

如果我没记错的话,VB 中的“Nothing”表示“默认值”。对于值类型,这是默认值,对于引用类型,这将是 null。因此,对结构不赋值完全没有问题。

【讨论】:

  • 这没有回答问题。
  • 不,它什么也澄清不了。问题全在于 VB 中的 &lt;&gt; 运算符,以及它如何对可空类型进行操作。
【解决方案6】:

这绝对是 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

【讨论】:

  • 不熟悉的时候是“怪事”。请参阅 Pieter Geerkens 给出的答案。
  • 嗯,我也觉得奇怪的是 VB 没有重现 Nullable&lt;&gt;.Equals() 的行为。人们可能期望它以相同的方式工作(这就是 C# 所做的)。
  • 期望,就像“一个人可能期望的”一样,是关于一个人所经历的。 C# 的设计考虑了 Java 用户的期望。 Java 的设计考虑了 C/C++ 用户的期望。无论好坏,VB.NET 在设计时都考虑到了 VB6 用户的期望。在stackoverflow.com/questions/14837209/…stackoverflow.com/questions/10176737/… 获得更多思考
  • @MatthewWatson Nullable 的定义在 .NET 的第一个版本中不存在,它是在 C# 和 VB.NET 出现一段时间后创建的,并且已经确定了它们的空传播行为.您是否真的希望该语言与几年后才创建的类型保持一致?从 VB.NET 程序员的角度来看,与语言不一致的是 Nullable.Equals,而不是相反。 (鉴于 C# 和 VB 都使用相同的 Nullable 定义,因此无法使其与两种语言保持一致。)
【解决方案7】:

您的 VB 代码完全不正确 - 如果您将“x y”更改为“x = y”,结果仍然是“false”。对于可空实例,最常见的 this 表达方式是“Not x.Equals(y)”,这将产生与 C# 中的“x != y”相同的行为。

【讨论】:

  • 除非xnothing,在这种情况下x.Equals(y) 会抛出异常。
  • @Servy:再次偶然发现这个问题(多年后),并注意到我没有纠正你 - “x.Equals(y)”将不会抛出一个可空类型实例“x”的异常。编译器会以不同的方式处理可空类型。
  • 具体来说,初始化为'null'的可空实例并不是真正设置为空的变量,而是没有设置值的System.Nullable实例。
猜你喜欢
  • 1970-01-01
  • 2010-09-27
  • 1970-01-01
  • 2017-08-14
  • 2011-11-03
  • 2011-10-24
  • 2019-03-07
  • 2018-11-18
  • 1970-01-01
相关资源
最近更新 更多