==とEquals()のC#の違い


548

Silverlightアプリケーションに2つの文字列を比較する条件があります。何らかの理由で、それを使用する==false.Equals()返され、trueが返されます

これがコードです:

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content.Equals("Energy Attack"))
{
    // Execute code
}

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content == "Energy Attack")
{
    // Execute code
}

これがなぜ起こっているのかについて何か理由がありますか?


2
次も参照してください:stackoverflow.com/questions/144530/or-equals
Arrow

8
文字列はをオーバーライドします==が、演算子はポリモーフィックではありません。このコードでは、==演算子はtype objectで呼び出され、値1ではなくID比較を行います。
ドリューノークス2014

12
@DrewNoakesのコメントを拡張するには:コンパイラー==は、オペランドのコンパイル時のタイプに基づいてオーバーロードを選択します。Contentプロパティがありますobject。演算子は仮想ではないため、のデフォルトの実装==が呼び出され、参照の等価性比較が行われます。Equalsでは、呼び出しは仮想メソッドに送られobject.Equals(object)ます。stringこのメソッドをオーバーライドし、文字列コンテンツに対して序数比較を実行します。msdn.microsoft.com/en-us/library/fkfd9eh8(v=vs.110).aspxおよびreferencesource.microsoft.com/#mscorlib/system/string.cs,507を参照してください
phoog、2015年

6
@phoogの説明は正確です。の左側に==コンパイル時の型がobjectあり、右側にコンパイル時の型stringがある場合、C#コンパイラは(この場合は問題のある)オーバーロードを選択する必要があることに注意してくださいoperator ==(object, object)。それがされます、それが意図しないすることができることをコンパイル時の警告を発行します。だから、読んで、コンパイル時の警告を!問題を修正して引き続きを使用する==には、左側をにキャストしstringます。私が正しく覚えている場合、警告テキストはそれを示唆しています。
Jeppe Stig Nielsen 2017

1
@JeppeStigNielsen +1(コンパイラの警告を読むためのアドバイス)。さらに良いのは、エラーとしての警告オプションをオンにして、全員に注意を向けさせることです。
phoog 2017年

回答:


429

とき==タイプの発現に使用されobject、それが解決しますSystem.Object.ReferenceEquals

Equalsは単なるvirtualメソッドであり、そのように動作するため、オーバーライドされたバージョンが使用されます(stringタイプでは内容が比較されます)。


57
オペレーターがクラスで具体的に実装されていない限り
Dominic Cronin

23
@DominicCroninこれは真実ではありません。==がクラスに実装されている場合でも、比較の左側の型がオブジェクトであるため、無視されます。演算子のオーバーロードはコンパイル時に決定されているように見え、コンパイル時にわかるのは、左側がオブジェクトであることだけです。
MikeKulls 2012

4
@DominicCronin ==はオブジェクトに解決されるという点で最初のステートメントは正しいと思いますが、演算子のオーバーロードが同様の方法で解決する2番目のステートメントは正しくありません。それらはまったく異なります。そのため、==はオブジェクトに解決されますが、.Equalsは文字列に解決されます。
MikeKulls

8
明確にするために、objectタイプ(モノスペースフォントに注意)は技術的には「タイプの表現」であることを意味しますSystem.Object。式によって参照されるインスタンスのランタイムタイプとは何の関係もありません。「ユーザー定義の演算子はvirtualメソッドのように扱われる」という文は非常に誤解を招くと思います。これらはオーバーロードされたメソッドのように扱われ、オペランドのコンパイル時の型にのみ依存します。実際、候補となるユーザー定義演算子のセットが計算された後、残りのバインド手順は、まさにメソッドオーバーロード解決アルゴリズムになります
Mehrdad Afshari

4
@DominicCronin誤解を招く部分は、virtualメソッドの解決はインスタンスの実際の実行時のタイプに依存するということですが、それはオペレーターのオーバーロードの解決では完全に無視され、それが実際に私の答えの要点です。
Mehrdad Afshari

314

オブジェクト参照を文字列と比較する場合(オブジェクト参照が文字列を参照している場合でも)、==文字列クラスに固有の演算子の特別な動作は無視されます。

(ある文字列を扱うない、)通常は、Equals比較値をしながら、==比較オブジェクト参照を。比較している2つのオブジェクトがオブジェクトの同じインスタンスを参照している場合、どちらもtrueを返しますが、一方が同じコンテンツであり、異なるソースからのものである場合(同じデータを持つ別のインスタンスである場合)、Equalsのみがtrueを返します。ただし、コメントに記載されているように、stringは特殊なケースです。stringは==演算子をオーバーライドするため、(オブジェクト参照ではなく)文字列参照のみを処理する場合、個別のインスタンスであっても値のみが比較されます。次のコードは、動作の微妙な違いを示しています。

string s1 = "test";
string s2 = "test";
string s3 = "test1".Substring(0, 4);
object s4 = s3;
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s3), s1 == s3, s1.Equals(s3));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s4), s1 == s4, s1.Equals(s4));

出力は次のとおりです。

True True True
False True True
False False True

8
スポット。'=='演算子はオブジェクト参照を比較し(浅い比較)、. Equals()はオブジェクトの内容を比較します(詳細な比較)。@mehrdadが言ったように、.Equals()はオーバーライドされ、その深いコンテンツ比較を提供します。
アンドリュー

1
起こっているのかを理解するために細心の注意を払わなければならないので、何が起こっていないのかを強調することは価値があると思うので、ここには投稿を残します。(。そして、私は正しいと間違った理解を実証するためのコードがあまりにも価値があると思います)私は評価は0を下回らないことを願っています
BlueMonkMN

5
確かに文字列はカスタム==演算子を実装します。そうでない場合、==を使用してもコンテンツは比較されません。Stringは、カスタムオペレーターが定義されていない一般的なケースを理解するのに役立たないため、ここで使用するのは悪い例です。
ドミニククロニン

6
叙事詩のコード例の+1。これは私にこれを理解させました。オブジェクトである静的タイプ(左手タイプ)の一般的なケースと、文字列である静的タイプ(/ RHSタイプ)の特定のケースを示します。そして、ストリングのインターンについても触れています。
barlop 14

2
@badsamaritan文字列のインターンのため
Alexander Derck 2017年

46

==また.Equals、どちらも実際のタイプで定義された動作と呼び出しサイトの実際のタイプに依存しています。どちらも単なるメソッド/演算子であり、任意の型でオーバーライドでき、作成者が望む動作を指定できます。私の経験では、人々が.Equalsオブジェクトに実装することは一般的ですが、operatorを実装することを怠っています==。これは、.Equalsが実際に値の同等性を==測定する一方で、それらが同じ参照であるかどうかを測定することを意味します。

定義が流動的である新しいタイプで作業しているとき、または一般的なアルゴリズムを書いているとき、ベストプラクティスは次のとおりです。

  • C#で参照を比較する場合は、Object.ReferenceEquals直接使用します(一般的なケースでは必要ありません)。
  • 使用する値を比較したい場合 EqualityComparer<T>.Default

使用法==があいまいであると感じる場合Object.Reference、コード内で等号を明示的に使用して、あいまいさを取り除きます。

Eric Lippertは最近、CLRに平等の2つの方法がある理由についてのブログ投稿を行いました。読む価値があります


Jaredさん、あなたはJeffの有名な「最高のコードはここではコードではありません」に直接違反しています。これは本当に正当化されますか?一方、これがどこから来ているのか、そしてセマ​​ンティクスを明示的にすることが望ましい理由がわかります。この場合、私はオブジェクトの等価性を扱うVBの方法を非常に好みます。短く明確です。
Konrad Rudolph、

@Konrad、私は本当に「タイプに慣れていないとき、私はベストプラクティスが次であることを見つける」と言ったはずです。はい、VBは値と参照の等価性を本当に分離しているため、ここではセマンティクスがはるかに優れています。C#はこの2つを混合しますが、あいまいなエラーが発生することがあります。
JaredPar 2009年

10
これは完全に真実ではありません。==オーバーライドできません。静的メソッドです。オーバーロードのみ可能であり、これは重要な違いです。したがって、==演算子に対して実行されるコードはコンパイル時にリンクされ、Equalsは仮想で実行時に検出されます。
Stefan Steinegger 2013

20

==演算子

  1. オペランドが値タイプであり、それらのが等しい場合、trueを返し、そうでない場合はfalseを返します。
  2. オペランドが文字列を除く参照型であり、両方が同じインスタンス(同じオブジェクト)を参照している場合、trueを返し、それ以外の場合はfalseを返します。
  3. オペランドが文字列型で、それらのが等しい場合、trueを返し、それ以外の場合はfalseを返します。

。等しい

  1. オペランドがある場合は参照型、それが実行リファレンス平等を両方とも同じを参照している場合であるインスタンス(同じオブジェクト)、それは本当それ以外の場合はfalseを返します。
  2. オペランドが値型の場合、==演算子とは異なり、最初に型をチェックし、が同じ場合は==演算子を実行します。それ以外の場合はfalseを返します。

2
これは正しくありません。==オペレータは、任意のタイプだけでなく、文字列のため、オーバーロードすることができます。文字列についてのみ特殊なケースの例外を記述すると、オペレーターのセマンティクスが誤って表現されます。「オペランドが参照型の場合、オペランドが同じオブジェクトを参照している場合、該当するオーバーロードがない限りtrueを返します。その場合、オーバーロードの実装によって結果が決定されます。 「。同じEqualsことが、仮想メソッドであるという複雑さが追加された場合にも当てはまるため、その動作をオーバーライドすることも、オーバーロードすることもできます。
phoog 2015年

19

まず、違いあります。数字について

> 2 == 2.0
True

> 2.Equals(2.0)
False

そして弦のために

> string x = null;
> x == null
True

> x.Equals(null)
NullReferenceException

どちらの場合も、==より便利に動作します.Equals


2
==演算子を使用した整数型から浮動小数点型への型強制が良いことだとは思いません。たとえば、16777216.0fは(int)16777217、(double)16777217.0、両方、またはどちらとも等しくないのでしょうか?整数型間の比較は問題ありませんが、浮動小数点比較は、一致する型に明示的にキャストされた値を使用してIMHOでのみ実行する必要があります。比較float以外にfloat、またはdouble以外にはdouble、診断なしでコンパイルべきではない主要なコードのにおいとして私を打ちます。
スーパーキャット2013

1
私は同意する-それの悲惨@supercat x == y意味するものではありませんx/3 == y/3(トライx = 5y = 5.0)を。
大佐パニック

/整数除算の使用は、C#およびJavaの設計における欠陥であると考えています。Pascal divおよびVB.NETの` are much better. The problems with == `はさらに悪いですが、それを意味するものではx==yありy==zませんx==z(前のコメントの3つの数値を考慮してください)。あなたがいても、示唆関係についてはxyの両方であるfloatかまたは両方doublex.equals((Object)y)ということを意味するものではありません1.0f/x == 1.0F / y`(;場合でも、私は私のdruthersを持っていた場合、それがいることを保証する==正区別し、ゼロ、しないEqualsはずです)。
スーパーキャット2013

Equals()の最初のパラメーターは文字列であるため、これは正常です。
むち打ち症

17

私が理解している限り、答えは簡単です:

  1. == オブジェクト参照を比較します。
  2. .Equals オブジェクトの内容を比較します。
  3. String データ型は常にコンテンツ比較のように機能します。

私が正しいことと、それがあなたの質問に答えてくれることを願っています。


15

オブジェクトを文字列にキャストすると、正しく機能することを付け加えます。これが、コンパイラが次の警告を出す理由です。

意図しない参照の比較の可能性。値を比較するには、左側をキャストして「文字列」と入力します


1
丁度。@DominicCronin:コンパイル時の警告に常に注意してください。ある場合object expr = XXX; if (expr == "Energy") { ... }、左側はコンパイル時の型objectであるため、コンパイラはオーバーロードを使用する必要がありoperator ==(object, object)ます。参照の等価性をチェックします。それは与えるかどうtrueか、false理由を予測することは難しいことができます文字列がインターン。左側がかタイプであることがわかっている場合は、を使用する前に左側をにキャストしてください。nullstringstring==
Jeppe Stig Nielsen

その一部を別の言い方で言います。==(参照の等価性と値の等価性のどちらを使用するかを決定する場合)は、コンパイル時のタイプ/静的タイプ/左側タイプによって異なります。(これは、コンパイル時の分析で解決されるタイプです)。実行時タイプ/動的タイプ/ RHSタイプではなく。BlueMonkMNのコードは、キャストではなく、それを示しています。
barlop 14

5

.Equalメソッドの静的バージョンはこれまで述べられていなかったので、ここに追加して、3つのバリエーションを要約して比較します。

MyString.Equals("Somestring"))          //Method 1
MyString == "Somestring"                //Method 2
String.Equals("Somestring", MyString);  //Method 3 (static String.Equals method) - better

where MyStringは、コードの他の場所から来る変数です。

背景情報と要約:

Javaでは==文字列の比較に使用することはできません。両方の言語を使用する必要がある場合==に備えて、C#では使用をより優れたものに置き換えることもできることを説明します。

C#では、どちらも文字列型である限り、方法1または方法2を使用して文字列を比較しても実用的な違いはありません。ただし、1つがnullである場合、1つが別のタイプ(整数など)である場合、または1つが別の参照を持つオブジェクトを表す場合、最初の質問が示すように、コンテンツが等しいかどうかを比較しても内容が返されない場合があります。あなたが期待します。

推奨される解決策:

==を使用.Equalsすることは、物事を比較するときに使用することとまったく同じではないため、代わりに静的なString.Equalsメソッドを使用できます。このようにして、2つのサイドが同じタイプでない場合でもコンテンツを比較し、一方がnullの場合は例外を回避します。

   bool areEqual = String.Equals("Somestring", MyString);  

書くのはもう少しですが、私の意見では、より安全に使用できます。

Microsoftからコピーした情報の一部を次に示します。

public static bool Equals (string a, string b);

パラメーター

a ストリング

比較する最初の文字列、またはnull

b ストリング

比較する2番目の文字列、またはnull

戻り値 Boolean

trueの値がの値とa同じ場合b。それ以外の場合false。両方の場合abされnull、メソッドが返しますtrue


5

すでに良い答えへの追加と同じように:この動作は文字列や異なる数値型の比較に限定されません。両方の要素が同じ基本型のオブジェクト型であっても。「==」は機能しません。

次のスクリーンショットは、2つのオブジェクト{int}-値を比較した結果を示しています

VS2017の例


2

ここでは少し混乱しています。Contentのランタイムタイプが文字列タイプの場合、==とEqualsはどちらもtrueを返す必要があります。ただし、これは当てはまらないように見えるため、コンテンツの実行時のタイプは文字列ではなく、その上でEqualsを呼び出すと参照等価が行われ、これがEquals( "Energy Attack")が失敗する理由を説明しています。ただし、2番目のケースでは、どのオーバーロードされた==静的演算子を呼び出すかに関する決定はコンパイル時に行われ、この決定は==(string、string)のように見えます。これは、Contentが暗黙的に文字列に変換することを示唆しています。


2
あなたはそれを背中合わせに持っています。開始の場合、Equals( "Energy Attack")は失敗しません。==はfalseを返します。==は文字列ではなく== fromオブジェクトを使用しているため失敗します。
MikeKulls、2011

デフォルトでは、演算子==は、2つの参照が同じオブジェクトを示しているかどうかを判別することにより、参照の等価性をテストします。したがって、この機能を利用するために、参照型に演算子==を実装する必要はありません。型が不変である場合、つまりインスタンスに含まれるデータを変更できない場合は、演算子==をオーバーロードして、参照の等価性ではなく値の等価性を比較すると便利です。同じ価値があるからです 不変タイプの演算子==をオーバーライドすることはお勧めできません。
Wajeed-MSFT 2011

2

@BlueMonkMNによる以前の回答には別の側面があります。追加の側面は、@ Drahcirのタイトルの質問に対する回答は、それが述べられているとおり、値に到達した方法にも依存するということstringです。説明する:

string s1 = "test";
string s2 = "test";
string s3 = "test1".Substring(0, 4);
object s4 = s3;
string s5 = "te" + "st";
object s6 = s5;
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2));

Console.WriteLine("\n  Case1 - A method changes the value:");
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s3), s1 == s3, s1.Equals(s3));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s4), s1 == s4, s1.Equals(s4));

Console.WriteLine("\n  Case2 - Having only literals allows to arrive at a literal:");
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s5), s1 == s5, s1.Equals(s5));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s6), s1 == s6, s1.Equals(s6));

出力は次のとおりです。

True True True

  Case1 - A method changes the value:
False True True
False False True

  Case2 - Having only literals allows to arrive at a literal:
True True True
True True True

2

答えにもう1つポイントを追加します。

.EqualsTo() メソッドは、文化と大文字小文字を区別するための比較を提供します。


0

==C#のトークンは、2つの異なる等価チェック演算子に使用されます。コンパイラーがそのトークンを検出すると、比較対象の型のいずれかが、比較対象の特定の組み合わせ型(*)、または両方の型に変換できる型の組み合わせに対して、等価演算子オーバーロードを実装しているかどうかをチェックします。コンパイラーがそのようなオーバーロードを見つけると、それを使用します。それ以外の場合、2つの型が両方とも参照型であり、それらが関連のないクラスではない場合(インターフェースであるか、関連するクラスである可能性があります)、コンパイラーは==参照比較演算子と見なされます。どちらの条件にも当てはまらない場合、コンパイルは失敗します。

他の一部の言語では、2つの等価性チェック演算子に別々のトークンを使用していることに注意してください。たとえば、VB.NETでは、=トークンは式内でオーバーロード可能な等価性チェック演算子のみにIs使用され、参照テストまたはnullテスト演算子として使用されます。=等価チェック演算子をオーバーライドしない型で使用すると、Is参照の等価またはnullのテスト以外の目的で使用しようとすると失敗します。

(*)一般に、型はそれ自体との比較のために等値をオーバーロードするだけですが、型が他の特定の型と比較するために等値演算子をオーバーロードすることは有用です。たとえば、intと比較するために等価演算子を定義することができました(そして、IMHOは定義しなければなりませんでした)float。そのため、16777217はそれ自体を16777216fに等しいと報告しません。現状では、そのような演算子は定義されていないため、C#はintto を昇格させfloat、等価チェック演算子が認識する前に16777216fに丸めます。次に、その演算子は2つの等しい浮動小数点数を確認し、行われた丸めを認識せずに等しいと報告します。


intとfloatの比較でfalseが返されるのではなく、F#が使用するアプローチが好まれます。これは、そのような比較をまったく許可しないというものです。次に、プログラマーは、値の型が異なるという事実を処理するかどうか、およびその処理方法を決定できます。時々 、すべての後、我々はそのためです扱いたい3に等しいものとして3.0f。すべてのケースで意図されていることをプログラマーに言わせる必要がある場合、デフォルトの動作がないため、デフォルトの動作が意図しない結果につながる危険はありません。
phoog 2015年

@phoog:私の個人的な感覚では、言語には等価テストの「通常の」手段で等価関係を実装し、そうでないオペランドのすべての組み合わせを禁止する必要があります。単純にそのような比較を禁止するのではなく、floatがintと一致する整数を正確に表すことを確認することにより、整数とfloatの間の言語チェックの同等性を持つ大きな利点は見られませんが、言語を実行させるよりも優れたアプローチを検討します比較前の不可逆変換。
スーパーキャット2015年

0

本当に素晴らしい答えと例!

2つの基本的な違いを追加したいのですが、

などの演算子==は多態ではありませんEqualsが、

その概念を念頭に置いて、例を考え出した場合(左側と右側の参照型を調べ、型に実際に==演算子がオーバーロードされており、Equalsがオーバーライドされているかどうかを確認/知ることにより)、正しい答えを得ることが確実です。


-1

オブジェクトを作成するとき、オブジェクトには2つの部分があり、1つはコンテンツであり、もう1つはそのコンテンツへの参照です。 ==コンテンツと参照の両方を比較します。 equals()コンテンツのみを比較する

http://www.codeproject.com/Articles/584128/What-is-the-difference-between-equalsequals-and-Eq


1
本当じゃない。場合ab、両方の文字列参照で、その後の結果は、a == b参照が同じオブジェクトを指しているかどうかには依存しません。
phoog 2015年

-2

==

==演算子は、任意の種類の2つの変数を比較するために使用でき、単にビットを比較します

int a = 3;
byte b = 3;
if (a == b) { // true }

注:intの左側にゼロがさらにありますが、ここではそれを気にしません。

int a(00000011)==バイトb(00000011)

==演算子は、変数内のビットのパターンのみを考慮します。

使用== 2つの参照(プリミティブ)がヒープ上の同じオブジェクトを参照する場合。

変数が参照であってもプリミティブであっても、ルールは同じです。

Foo a = new Foo();
Foo b = new Foo();
Foo c = a;

if (a == b) { // false }
if (a == c) { // true }
if (b == c) { // false }

a == cはtrue a == bはfalse

aとcのビットパターンは同じであるため、==を使用すると同じになります。

等しい():

equals()メソッドを使用して、2つの異なるオブジェクトが等しいかどうかを確認します

どちらも「ジェーン」の文字を表す2つの異なるStringオブジェクトなど


2
これは誤りです。次のことを考慮してくださいobject a = 3; object b = 3; Console.WriteLine(a == b);。値のビットパターンが同じであっても、出力はfalseです。オペランドのタイプも重要です。この例でゼロの数が異なることに「気にしない」理由は、等号演算子を呼び出すときまでに、暗黙的な変換によりゼロの数は実際には同じであるためです。
phoog 2015年

-2

Equalと==の唯一の違いは、オブジェクトタイプの比較です。参照型と値型などの他の場合では、それらはほとんど同じです(両方ともビット単位で等しいか、両方とも参照で等しい)。

オブジェクト:等しい:ビット単位の等価性==:参照の等価性

string:(equalsと==はstringと同じですが、stringの1つがオブジェクトに変更された場合、比較結果は異なります)Equals:ビットごとの等価==:ビットごとの等価

詳細はこちらをご覧ください。


Object.Equalsは必ずしもビット単位の等価性を調べるわけではありません。これは仮想メソッドであり、オーバーライドは必要なことを何でも実行できます。
phoog

はい、そうです、あなたはそれを上書きしたいことを何でもすることができます。しかし、私たちが話しているトピックはデフォルトの実装です。Object.Equalsのデフォルトの実装はビット単位の等価です。
ウィル・ユー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.