113

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

なぜ違いがあるのですか?

4

7 に答える 7

90

VB.NET と C#.NET は異なる言語であり、使用法について異なる想定を行った異なるチームによって構築されています。この場合、NULL 比較のセマンティクスです。

私の個人的な好みは、VB.NET セマンティクスです。これは、本質的に NULL に「まだわかりません」というセマンティクスを与えます。それから5の「まだわからない」との比較。当然「まだわからない」です。つまりNULLです。これには、(すべてではないにしてもほとんどの) SQL データベースで NULL の動作をミラーリングするという追加の利点があります。here で説明されているように、これは 3 値ロジックの (C# よりも) 標準的な解釈でもあります。

C# チームは、NULL が何を意味するかについてさまざまな仮定を作成したため、表示される動作の違いが生じました。Eric Lippert は、C# における NULL の意味についてブログを書きました。Eric Lippert 氏によると、「VB / VBScript と JScript の null のセマンティクスについては、ここここに」.

NULL 値が可能な環境では、排他的中間の法則 (つまり、A または ~A がトートロジー的に真である) はもはや当てにできないことを認識することが重要です。

アップデート:

A bool( a とは対照的にbool?) は、値 TRUE および FALSE のみを取ることができます。ただし、NULL の言語実装では、式を通じて NULL がどのように伝播するかを決定する必要があります。VB では、式5=null5<>null両方が false を返します。C# では、比較可能な式の5==nullうち、最初から2 番目5!=nullのみ[2014 年 3 月 2 日更新 - PG]は false を返します。ただし、null をサポートするすべての環境では、その言語で使用される真理値表と null 伝播を知ることはプログラマーの義務です。

アップデート

セマンティクスに関する Eric Lippert のブログ記事 (以下のコメントで言及) は、現在次の場所にあります。

于 2013-03-20T12:56:55.170 に答える
37

の代わりに をx <> y返すためです。が定義されていないため、単に定義されていません。(SQL null に似ています)。Nothingtruex

注: 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

VB.NET言語仕様:

7.1.1 Null 許容値型 ... Null 許容値型には、null 値だけでなく、null 非許容バージョンの型と同じ値を含めることができます。したがって、null 許容値型の場合、その型の変数に Nothing を代入すると、変数の値が値型のゼロ値ではなく、null 値に設定されます。

例えば:

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) '
于 2013-03-20T12:55:46.293 に答える
17

Look at the generated CIL (I've converted both to 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");
    }
}

You'll see that the comparison in Visual Basic returns Nullable<bool> (not bool, false or true!). And undefined converted to bool is false.

Nothing compared to whatever is always Nothing, not false in Visual Basic (it is the same as in SQL).

于 2013-03-20T12:58:48.287 に答える
6

ここで観察される問題は、より一般的な問題の特殊なケースです。つまり、少なくともいくつかの状況で役立つ可能性のあるさまざまな平等の定義の数が、それらを表現するために一般的に利用できる手段の数を超えているということです。この問題は、場合によっては、等価性をテストするさまざまな手段が異なる結果をもたらすことは混乱を招くという不幸な信念によって悪化します。このような混乱は、可能な限りさまざまな形式の等価性が同じ結果をもたらすようにすることで回避できる可能性があります。

実際には、混乱の根本的な原因は、さまざまなセマンティクスがさまざまな状況で役立つという事実にもかかわらず、さまざまな形式の等式および不等式テストが同じ結果をもたらすと予想されるべきであるという誤った信念です。たとえば、算術の観点からするとDecimal、末尾のゼロの数だけが異なるものを等しいと比較できると便利です。double正のゼロや負のゼロなどの値についても同様です。一方、キャッシングまたはインターンの観点からは、そのようなセマンティクスは致命的となる可能性があります。たとえば、 と等しいはずの suchDictionary<Decimal, String>があるとします。そのようなオブジェクトは、多くのオブジェクトがあれば合理的に見えるでしょう。myDict[someDecimal]someDecimal.ToString()Decimal文字列に変換したい値で、多くの重複があると予想される値。残念ながら、このようなキャッシュを使用して 12.3 m と 12.40 m を変換し、その後に 12.30 m と 12.4 m を変換すると、後者の値は「12.30」と「12.4」ではなく「12.3」と「12.40」になります。

手元の問題に戻ると、null 許容オブジェクトが等しいかどうかを比較する賢明な方法は複数あります。C# は、その==演算子が の動作を反映する必要があるという立場を取りますEqualsVB.NETは、その動作が他の言語の動作を反映するべきであるという立場を取りEqualsますEquals。ある意味では、正しい解決策は、3 通りの "if" 構造を持ち、条件式が 3 つの値の結果を返す場合、その場合に何が起こるかをコードで指定する必要があることnullです。それはそのままの言語では選択肢にならないため、次善の代替策は、さまざまな言語がどのように機能するかを学び、それらが同じではないことを認識することです。

ちなみに、C にはない Visual Basic の "Is" 演算子を使用して、null 許容オブジェクトが実際に null かどうかをテストできます。ifテストで を受け入れるべきかどうか疑問に思う人もいるかもしれませんが、null 許容型で呼び出された場合ではなくBoolean?、通常の比較演算子が返されることは便利な機能です。ちなみに、VB.NET では、 ではなく等値演算子を使用しようとすると、比較の結果が常に になるという警告が表示されます。何かが null かどうかをテストする場合は、使用する必要があります。Boolean?BooleanIsNothingIs

于 2013-03-20T20:47:33.277 に答える
3

この 投稿があなたに役立つかもしれません:

私の記憶が正しければ、VB の「Nothing」は「デフォルト値」を意味します。値型の場合は既定値であり、参照型の場合は null になります。したがって、構造体に何も割り当てなくてもまったく問題ありません。

于 2013-03-20T12:55:07.207 に答える
2

これは VB の明確な奇妙さです。

VB では、2 つの null 許容型を比較す​​る場合は、 を使用する必要があります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
于 2013-03-20T12:59:47.277 に答える
0

VBコードは単純に正しくありません。「x<>y」を「x=y」に変更しても、結果として「false」が残ります。null許容インスタンスのこれを表現する最も一般的な方法は「Notx.Equals(y)」であり、これにより、C#の「x!=y」と同じ動作が得られます。

于 2013-03-20T15:44:11.283 に答える