C#が2つのオブジェクトタイプを比較できないが、VBはそうでないのはなぜですか?


152

C#には2つのオブジェクトがあり、それがブール型か他の型かわかりません。しかし、これらのC#を比較しようとすると、正しい答えが得られません。私はVB.NETで同じコードを試しましたが、それで終わりました!

誰かが解決策がある場合にこれを修正する方法を教えてもらえますか?

C#:

object a = true;
object b = true;
object c = false;
if (a == b) c = true;
MessageBox.Show(c.ToString()); //Outputs False !!

VB.NET:

Dim a As Object = True
Dim b As Object = True
Dim c As Object = False
If (a = b) Then c = True
MessageBox.Show(c.ToString()) '// Outputs True

3
等価比較子をに変更するとどうなりますa.Equals(b)か?
Jason Meckley、2013

8
これは教育目的のための良い質問です。
Lobo

10
VB.NETコードがC#コードと異なるためです。
セキュリティハウンド

9
に割り当てるとa、ボクシングを取得し、を含むボックスを作成しますtrue。に割り当てると、bを含む別のボックスが表示されますtrue。とを比較するab、どちらもコンパイル時のタイプであるため、C#言語仕様で定義されてobjectいるオーバーロードを呼び出しますoperator ==(object, object)。このオーバーロードは、参照が同じオブジェクトを指すかどうかを確認します。あなたが持っているので、2箱を、結果はfalse、そしてあなたの「下」文if意志は実行されません。これをよりよく理解するには、の割り当てbをこれに変更してみてobject b = a;ください。これでボックスが1つだけになりました。
Jeppe Stig Nielsen

3
私は以前、「VB.NETとC#が同じ言語であり、異なるアクセントで話されていると仮定して注意してください-そうではありません」
AakashM

回答:


168

C#では、==演算子(参照型式に適用される場合)がオーバーロードされない限り、参照の等価性チェックを実行します。ボクシング変換の結果である2つの参照を比較しているため、これらは別個の参照です。

編集:をオーバーロードする型を使用すると==、さまざまな動作を得ることができますが、それは式のコンパイル時の型に基づいています。たとえば、)をstring提供します==(string, string

string x = new string("foo".ToCharArray());
string y = new string("foo".ToCharArray());
Console.WriteLine(x == y); // True
Console.WriteLine((object) x == (object) y); // False

ここでは、最初の比較はオーバーロードされた演算子を使用していますが、2番目は「デフォルト」の参照比較を使用しています。

VBでは、=演算子はより多くの作業をobject.Equals(x, y)行いOption Compareます。たとえば、テキストの比較方法に影響を与える可能性があるため、を使用することと同等ではありません。

基本的に、オペレーターは同じように機能するわけではなく、同じように機能すること意図していません。


17
+1私はあなたが周りにいることを知っていました、あなたはこれらの種類の神秘的な質問を愛しています:)
アブドゥサラムベンハイ

3
@AbZy:=VB で何が行われたかについてより詳細な説明を提供できるようになりたいと思っていますが、仕様はあまり明確ではありません。
Jon Skeet、2013

興味深いことですが、オブジェクトを動的に変更すると、VBと同じように動作します
VladL 2013

4
@VladL:はい、それは実行時間のタイプによって行われ、bool == bool比較を実行するためです。
Jon Skeet

1
@Mahdi Loboはコードを提供した可能性がありますが、Jonの場合とは異なり、彼の答えも間違っています。
2013

79

物事のC#側を説明するジョンの回答に加えて、VBの機能は次のとおりです。

を使用したVB Option Strict Onでは、viaによる比較は= 常に値の等価性をテストし、参照の等価性はテストしません。実際、はを定義していないため、切り替えOption Strict Onた後でもコードはコンパイルされSystem.ObjectませんOperator=常にこのオプションをオンにしておく必要あります。ビーナスフライトラップよりも効果的にバグをキャッチします(特定のケースでは、この緩い動作が実際に正しいことを行います)。1

で実際には、Option Strict On、VBはC#よりもさらに厳しい動作します。では、C#のa == b いずれかへの呼び出しをトリガしSomeType.operator==(a, b)、これが存在しない場合、または、(呼び出すことと同じです参照の等価比較が起動しますobject.ReferenceEquals(a, b))。

一方、VBでは、比較はa = b 常に等価演算子を呼び出します。2参照の等価比較を使用する場合は、使用する必要がありますa Is b(これも、と同じですObject.ReferenceEquals(a, b))。


1)使用した理由はここには良い兆候だOption Strict Off悪い考えです:私は、.NETの公式リリース前から数年前までは、約10年のためのVB.NETを使用しました、と私は絶対にないアイデアきませんa = bでありませんがOption Strict Off。ある種の同等性比較を行いますが、正確に何が起こり、なぜそうなのかわかりません。dynamicただし、C#の機能よりも複雑です(十分に文書化されたAPIに依存しているため)。MSDNの内容は次のとおりです。

のでOption Strict On提供し、強い型付けを、データ損失の意図しない型変換、遅延バインディング禁止しを防ぎ、パフォーマンスが向上し、その使用を強くお勧めします。

2) Jonは、1つの例外である文字列について言及しました。文字列では、下位互換性のために、等価比較でさらに多くのことが行われます。


4
+1。これは、VB.NETの設計者がVB6とVBAのプログラマーにとって言語を「正しく機能させる」ことに成功した1つのケースだと思います。OOPはそれほど目立たないため、参照の等価性の概念はそれほど重要ではありません。VBコーダーは、オブジェクトなどについてあまり考えなくても、適切に機能するコードを作成できます。
ジョンMガント2013

5
+1これは実際に必要なほど賛成されているわけではありません。使用Option Strict Onしないことは犯罪と見なされる必要があります...
ディアハンター

1
@JohnMGant:参照IDの重要性を理解していないコーダーは、たまたま機能するコードを書くことができるかもしれませんが、何が安全に変更できるか、どのような変更が常に物事を壊すか、どのような変更が起こり得るかを実際に知ることはほとんどありません機能しているように見えますが、望ましくない厄介な副作用を引き起こします(たとえば、同じ状態を持つ異なる可変オブジェクトへの参照を、同じオブジェクトへの参照にする必要があります)。オブジェクトがめったに変更されない場合、このような変更によってすぐに問題が発生することはありませんが、後で見つけにくいバグが発生する可能性があります。
スーパーキャット2013

4

オブジェクトインスタンスは、演算子「==」と比較されません。メソッド「等しい」を使用する必要があります。「==」演算子を使用すると、オブジェクトではなく参照を比較します。

これを試して:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }
}

MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

結果:

a reference is not equal to b reference
a object is not equal to b object

今、これを試してください:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }

    public bool Equals(MyObject o)
    {
        return (Value.CompareTo(o.Value)==0);
    }
}
MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

結果:

a reference is not equal to b reference
a object is equal to b object

1
これは単にをオーバーライドしなかったためですoperator ==。その演算子をオーバーライドし、等しくない場合、出力は逆になります。の参照の比較に固有のものはなく、のoperator ==値の比較に固有のものもありませんEquals。これらは、平等を決定する2つの方法にすぎません。どちらにも参照比較のデフォルトの実装があり、両方をオーバーライドして、やりたいことをすべて実行できます。他の唯一の違いは、それEqualsが仮想であり、そうでoperator ==はないということです。
2013

1
@Servy:オーバーライド できないことに注意してください==- オーバーロードしかできません。
Jon Skeet、2013

1
すみません、-1。この答えは単に間違っており、受け入れられるべきではありません。
Konrad Rudolph、

どこかに、この答えを待っているJavaの質問があります。
チャドショギンズ2013

3

問題は、C#の==演算子が、2つのパラメーターのコンパイル時の型に基づく静的メソッドの呼び出し(技術的にはそうではないかもしれませんが、そうである可能性があります)です。それらのオブジェクトの実際の実行時の型が何であるかは重要ではありません。

そのコンパイル時のタイプに基づいて、コンパイラーは使用する実装を決定operator ==します。デフォルトのobject実装を使用する場合もあれば、言語によって提供される数値オーバーロードの1つを使用する場合もあれば、ユーザー定義の実装の場合もあります。

これはVBとは異なり、VBはコンパイル時に実装を決定しません。実行時まで待機し、指定された2つのパラメーターを検査して、==使用する演算子の実装を決定します。

コードにブール値が含まれていますが、それらは型の変数にありますobject。ので、変数が型であるobject、C#コンパイラは、使用objectの実装==を比較、参照、ないオブジェクトのインスタンスを。ブール値はボックスであるため、値が同じであっても同じ参照はありません。

VBコードは、変数のタイプを気にしません。実行時まで待機してから2つの変数をチェックし、それらが実際には両方ともブール型であることを確認して、ブール==演算子の実装を使用します。その実装は、参照ではなくブール値を比較します(ブール値はその演算子を呼び出す前にボックス化されないため、参照比較はもはや意味がありません)。ブール値は同じなので、trueを返します。


これはC#には問題ありません。=VBで何が行われているのかを正確に知るには十分な知識がありません。
Jon Skeet

@JonSkeet十分に公正です。
2013

パーmsdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx、セクションの「リレーショナル・比較演算子と型なしのプログラミング」: =、のような他のすべてのリレーショナル比較演算子と一緒に<>=など、演算子の両側または片側がである場合、特別な扱いが与えられObjectます。この特別な処理はVariant、pre.NET VBで知られているタイプを使用することに慣れているVB6プログラマーが、以前Objectと同じようにVB.Netで利用できるようにするために行われますVariant
rskar

別の言い方をすれば、オーバーロードとの影響は別としてOption Strict On、VB =Object、ストリングまたは数値に到達できるまで、アンボックス化するようにバイアスされています。
rskar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.