web-dev-qa-db-ja.com

==と等しい()のC#の違い

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

これがコードです:

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

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

なぜこれが起こっているのかという理由は何ですか?

499
Drahcir

==object型の式に使用されると、 System.Object.ReferenceEquals に解決されます。

Equals は単なるvirtualメソッドであり、そのように振る舞うので、オーバーライドされたバージョンが使用されます(string型の場合は内容を比較します)。

398
Mehrdad Afshari

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

通常(つまり、文字列を扱っていない場合)、Equals値を比較し==はオブジェクト参照を比較します比較している2つのオブジェクトが同じオブジェクトの正確なインスタンスを参照している場合、両方ともtrueを返しますが、一方が同じコンテンツを持ち、別のソースから取得された場合(同じデータを持つ個別のインスタンス)真を返します。ただし、コメントに記載されているように、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
280
BlueMonkMN

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

定義が流動的であるか、または一般的なアルゴリズムを書いている新しい型で作業しているとき、私はベストプラクティスが以下であるとわかります

  • C#で参照を比較したい場合は、Object.ReferenceEqualsを直接使用します(一般的な場合は不要です)。
  • 値を比較したい場合はEqualityComparer<T>.Defaultを使います

==の使い方があいまいであると感じる場合があるので、あいまいさを取り除くためにコードで明示的にObject.Reference equalsを使用します。

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

44
JaredPar

まず、の違いです。数字の場合

> 2 == 2.0
True

> 2.Equals(2.0)
False

そして文字列のために

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

> x.Equals(null)
NullReferenceException

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

18
Colonel Panic

オブジェクトを文字列にキャストしても正しく動作することを付け加えます。これが、コンパイラがあなたに次のような警告を出す理由です。

意図しない参照の比較。値を比較するには、左側を型 'string'にキャストします。

13
MikeKulls

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

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

私は正しいと私はあなたの質問に答えたことを願っています。

9

==演算子1.オペランドが 値型 で、値が等しい場合はtrueを返します。偽です。 2.オペランドが 参照型 で、stringが例外で、両方とも同じオブジェクトを参照している場合、trueを返し、それ以外の場合はfalseを返します。 3.オペランドが文字列型で値が等しい場合はtrue、それ以外の場合はfalseを返します。

。等号1.オペランドが参照型の場合、 参照等価 を実行します。同じオブジェクトであればtrue、そうでなければfalseを返します。 2.オペランドが値型の場合、==演算子とは異なり、最初にそれらの型をチェックし、それらの型が同じ場合は==演算子を実行し、それ以外の場合はfalseを返します。

9
kashif

静的バージョンの.Equalメソッドはこれまでのところ触れられていないので、ここでこれを追加して3つのバリエーションをまとめて比較します。

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

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

背景情報と要約すると:

Javaでは、文字列を比較するために==を使用するべきではありません。両方の言語を使用する必要がある場合、および==を使用することをC#のより優れたものに置き換えることができることを知らせるためにも、このことについて言及します。

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

推奨される解決策:

比較するときに==を使用することは.Equalsを使用することとまったく同じではないため、代わりに静的String.Equalsメソッドを使用できます。このようにして、両側が同じ型でない場合でもコンテンツを比較し、一方が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。それ以外の場合はfalseabの両方がnullである場合、メソッドはtrueを返します。

4
levteck

@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
Novichok

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

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

2
Bala

私はここで少し混乱しています。 Contentの実行時型が文字列型の場合は、==とEqualsの両方がtrueを返すはずです。しかし、これは当てはまらないようなので、実行時のタイプのコンテンツは文字列ではなく、その上でEqualsを呼び出すと参照等価になります。これが、Equals( "Energy Attack")が失敗する理由の説明です。しかし、2番目のケースでは、どのオーバーロードされた== static operatorを呼び出すべきかについての判断がコンパイル時に行われ、この判断は==(string、string)のように見えます。これは、Contentが暗黙的に文字列に変換することを示唆しています。

2
Mehmet Aras

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

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

Example From VS2017

1
Ole Albers

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

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

(*)通常、型はそれ自身と比較するために等価性をオーバーロードするだけですが、型が他の特定の型と比較するために等価演算子をオーバーロードするのに役立ちます。例えば、intfloatと比較するために等価演算子を定義することができ(そして私見ではそうすべきではありませんが)、16777217はそれ自体16777216fと等しい値を報告しません。そのような演算子が定義されていないので、C#はintfloatに昇格させ、等価検査演算子がそれを見る前に16777216fに丸めます。その演算子は2つの等しい浮動小数点数を見て、発生した丸めに気付かずに等しいと報告します。

0
supercat

本当にすばらしい答えと例!

この2つの間に根本的な違いを付け加えたいと思います。

==のような演算子は多相ではありませんが、Equals

その概念を念頭に置いて、(左手と右手の参照型を見て、その型が実際に==演算子がオーバーロードされ、Equalsがオーバーライドされているかどうかを確認/確認することによって)例を考えれば正しい答えが得られます。 。

0