Silverlightアプリケーションでは、2つの文字列を比較する条件があります。何らかの理由で==
を使用するとfalseが返され、.Equals()
はtrueを返します。
これがコードです:
if (((ListBoxItem)lstBaseMenu.SelectedItem).Content.Equals("Energy Attack"))
{
// Execute code
}
if (((ListBoxItem)lstBaseMenu.SelectedItem).Content == "Energy Attack")
{
// Execute code
}
なぜこれが起こっているのかという理由は何ですか?
==
がobject
型の式に使用されると、 System.Object.ReferenceEquals
に解決されます。
Equals
は単なるvirtual
メソッドであり、そのように振る舞うので、オーバーライドされたバージョンが使用されます(string
型の場合は内容を比較します)。
オブジェクト参照を文字列と比較する場合(オブジェクト参照が文字列を参照している場合でも)、文字列クラスに固有の==
演算子の特殊な動作は無視されます。
通常(つまり、文字列を扱っていない場合)、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
==
と.Equals
は、どちらも実際の型で定義されている動作と呼び出しサイトでの実際の型に依存しています。どちらも単にメソッド/演算子であり、任意の型でオーバーライドすることができ、作成者が望む任意の動作を指定できます。私の経験では、オブジェクトに.Equals
を実装することは一般的ですが、演算子==
を実装することは怠っています。これは、.Equals
が実際に値の同等性を測定するのに対し、==
はそれらが同じ参照であるかどうかを測定することを意味します。
定義が流動的であるか、または一般的なアルゴリズムを書いている新しい型で作業しているとき、私はベストプラクティスが以下であるとわかります
Object.ReferenceEquals
を直接使用します(一般的な場合は不要です)。EqualityComparer<T>.Default
を使います==
の使い方があいまいであると感じる場合があるので、あいまいさを取り除くためにコードで明示的にObject.Reference
equalsを使用します。
Eric Lippertは最近、CLRに2つの同等性の方法がある理由についてのブログ投稿を行いました。読む価値がある
まず、はの違いです。数字の場合
> 2 == 2.0
True
> 2.Equals(2.0)
False
そして文字列のために
> string x = null;
> x == null
True
> x.Equals(null)
NullReferenceException
どちらの場合も、==
は.Equals
よりも便利に動作します。
オブジェクトを文字列にキャストしても正しく動作することを付け加えます。これが、コンパイラがあなたに次のような警告を出す理由です。
意図しない参照の比較。値を比較するには、左側を型 'string'にキャストします。
私が理解している限りでは、答えは簡単です。
私は正しいと私はあなたの質問に答えたことを願っています。
==演算子1.オペランドが 値型 で、値が等しい場合はtrueを返します。偽です。 2.オペランドが 参照型 で、stringが例外で、両方とも同じオブジェクトを参照している場合、trueを返し、それ以外の場合はfalseを返します。 3.オペランドが文字列型で値が等しい場合はtrue、それ以外の場合はfalseを返します。
。等号1.オペランドが参照型の場合、 参照等価 を実行します。同じオブジェクトであればtrue、そうでなければfalseを返します。 2.オペランドが値型の場合、==演算子とは異なり、最初にそれらの型をチェックし、それらの型が同じ場合は==演算子を実行し、それ以外の場合はfalseを返します。
静的バージョンの.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
。それ以外の場合はfalse
。 a
とb
の両方がnull
である場合、メソッドはtrue
を返します。
@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
もう1点答えにポイントを追加します。
.EqualsTo()
メソッドは、文化と大文字と小文字を区別して比較するための準備を提供します。
私はここで少し混乱しています。 Contentの実行時型が文字列型の場合は、==とEqualsの両方がtrueを返すはずです。しかし、これは当てはまらないようなので、実行時のタイプのコンテンツは文字列ではなく、その上でEqualsを呼び出すと参照等価になります。これが、Equals( "Energy Attack")が失敗する理由の説明です。しかし、2番目のケースでは、どのオーバーロードされた== static operatorを呼び出すべきかについての判断がコンパイル時に行われ、この判断は==(string、string)のように見えます。これは、Contentが暗黙的に文字列に変換することを示唆しています。
C#の==
トークンは、2つの異なる等価性検査演算子に使用されます。コンパイラがそのトークンに遭遇すると、比較されている特定の組み合わせ型(*)、または両方の型が変換可能な型の組み合わせについて、比較されている型のいずれかが等価演算子オーバーロードを実装しているかどうかをチェックします。コンパイラがそのようなオーバーロードを見つけた場合は、それを使用します。そうでなければ、2つの型が両方とも参照型であり、それらが無関係のクラスではない場合(インタフェースである場合も、関連クラスである場合もあります)、コンパイラは==
を参照比較演算子と見なします。どちらの条件も当てはまらない場合、コンパイルは失敗します。
他の言語の中には、2つの等価検査演算子に別々のトークンを使用するものがあることに注意してください。たとえばVB.NETでは、=
トークンは、オーバーロード可能な等価性検査演算子のためだけに式の中で使用され、Is
は参照テストまたはnullテスト演算子として使用されます。等価性チェック演算子をオーバーライドしない型で=
を使用すると失敗します。参照の等価性または無効性のテスト以外の目的でIs
を使用しようとすると失敗します。
(*)通常、型はそれ自身と比較するために等価性をオーバーロードするだけですが、型が他の特定の型と比較するために等価演算子をオーバーロードするのに役立ちます。例えば、int
はfloat
と比較するために等価演算子を定義することができ(そして私見ではそうすべきではありませんが)、16777217はそれ自体16777216fと等しい値を報告しません。そのような演算子が定義されていないので、C#はint
をfloat
に昇格させ、等価検査演算子がそれを見る前に16777216fに丸めます。その演算子は2つの等しい浮動小数点数を見て、発生した丸めに気付かずに等しいと報告します。
本当にすばらしい答えと例!
この2つの間に根本的な違いを付け加えたいと思います。
==
のような演算子は多相ではありませんが、Equals
は
その概念を念頭に置いて、(左手と右手の参照型を見て、その型が実際に==演算子がオーバーロードされ、Equalsがオーバーライドされているかどうかを確認/確認することによって)例を考えれば正しい答えが得られます。 。