翻訳者から:
これは、以前はC#開発者の1人であったEric Lippertのブログ 投稿の無料翻訳です。 記録は「質問と回答」の形式です。質問をスキップして回答に進みます。元の質問に慣れることができますが、特に興味深いものはありません。
ただし、まずは次のコードをご覧ください。Googleとコンパイルを行わずに、比較の9つのケースで何が起こるかを調べ、回答を確認してください(アンケートの場合)。
int myInt = 1; short myShort = 1; object objInt1 = myInt; object objInt2 = myInt; object objShort = myShort; Console.WriteLine(myInt == myShort); // scenario 1 Console.WriteLine(myShort == myInt); // scenario 2 Console.WriteLine(myInt.Equals(myShort)); // scenario 3 Console.WriteLine(myShort.Equals(myInt)); // scenario 4 Console.WriteLine(objInt1 == objInt1); // scenario 5 Console.WriteLine(objInt1 == objShort); // scenario 6 Console.WriteLine(objInt1 == objInt2); // scenario 7 Console.WriteLine(Equals(objInt1, objInt2)); // scenario 8 Console.WriteLine(Equals(objInt1, objShort)); // scenario 9
C#言語は、開発者が期待するとおりに動作するように設計されています。つまり、 明らかなテクニックと正しいテクニックがまったく同じ言語です。 そして大部分はそうです。 残念ながら、比較はトラップのある言語の一部です。
次のコードを作成して、さまざまな程度の比較を示します。
答え
int myInt = 1; short myShort = 1; object objInt1 = myInt; object objInt2 = myInt; object objShort = myShort; Console.WriteLine(myInt == myShort); // scenario 1 true Console.WriteLine(myShort == myInt); // scenario 2 true Console.WriteLine(myInt.Equals(myShort)); // scenario 3 true Console.WriteLine(myShort.Equals(myInt)); // scenario 4 false! Console.WriteLine(objInt1 == objInt1); // scenario 5 true Console.WriteLine(objInt1 == objShort); // scenario 6 false!! Console.WriteLine(objInt1 == objInt2); // scenario 7 false!!! Console.WriteLine(Equals(objInt1, objInt2)); // scenario 8 true Console.WriteLine(Equals(objInt1, objShort)); // scenario 9 false!?!
一体何? 順番に見てみましょう。
最初のケースと2番目のケースでは、 最初に
==
演算子の意味を決定する必要があります。 C#には、さまざまな型を比較するための十数個の組み込み
==
演算子があります。
object == object string == string int == int uint == uint long == long ulong == ulong ...
int == short
または
short == int
演算子がないため、最適な演算子を選択する必要があります。 私たちの場合、これは
int == int
ステートメントです。 したがって、
short
int
変換され、2つの変数が値で比較されます。 したがって、それらは同等です。
3番目の場合、オーバーロードされたEqualsメソッドのどれを呼び出すかを最初に決定する必要があります。 呼び出し元のインスタンスは
int
型であり、3つの
Equals
メソッドを実装しています。
Equals(object, object) // object Equals(object) // object Equals(int) // IEquatable<int>.Equals(int)
前者は、それを呼び出すのに十分な引数がないため、私たちには適していません。 他の2つのメソッドの中で、パラメーターとして
int
を受け入れるメソッドの方が適しています;常に
short
型の引数を
object
に変換するよりも
int
に変換する方が適切です。 したがって、値による比較を使用して
int
型の2つの変数を比較する
Equals(int)
が呼び出されるため、この式はtrueです。
4番目のケースでは、どの
Equals
メソッドが呼び出されるかを再度決定する必要があります。 呼び出し元のインスタンスの型は
short
、これには3つの
Equals
メソッドがあります。
Equals(object, object) // object Equals(object) // object Equals(short) // IEquatable<short>.Equals(short)
最初のメソッドと3番目のメソッドは、引数が少なすぎるため、適切ではありません。また、
int
型の
short
への暗黙的なキャストがないため、3番目のメソッドは選択されません。
short.Equals(object)
メソッドが残り、その実装は次のコードと同じです。
bool Equals(object z) { return z is short && (short)z == this; }
つまり、このメソッドが
true
を返すためには、 パッケージ化された要素は
short
型である必要があり、展開後は
Equals
を呼び出したインスタンスと等しくなければなりません。 ただし、渡された引数は
int
であるため、メソッドは
false
を返し
false
。
5番目 、 6番目 、 7番目では、
object == object
比較フォームが選択されます。これは、
Object.ReferenceEquals
メソッドを呼び出すのと
Object.ReferenceEquals
です。 明らかに、2つのリンクは5番目のケースでは等しく、 6番目と7 番目のケースでは等しくありません。 タイプ
object
変数に含まれる値
object
重要で
object
ません。値による比較はまったく使用されず、リンクのみが比較されるためです。
8番目と9番目のケースでは、静的メソッド
Object.Equals
が使用され、次のように実装されます。
public static bool Equals(object x, object y) { if (ReferenceEquals(x, y)) return true; if (ReferenceEquals(x, null)) return false; if (ReferenceEquals(y, null)) return false; return x.Equals(y); }
8番目のケースでは、
null
と等しくない2つのリンクがあるため、
int.Equals(object)
が呼び出されます。これは、
short.Equals(object)
メソッドのコードを見ればわかるように、次のように実装されます。
bool Equals(object z) { return z is int && (int)z == this; }
引数は
int
型のパック変数であるため、値によって比較が行われ、メソッドは
true
を返し
true
。 9番目のケースでは、パック変数の型は
short
であるため、型のチェック(
z is int
)は失敗し、メソッドは
false
を返し
false
。
結果 :
2つの変数を比較するための9つの異なる方法を示しましたが、すべての場合において、比較される変数は1に等しく、半分の場合にのみ比較が
true
返し
true
。 これがおかしくて、すべてが混乱していると思うなら、あなたは正しいです! C#での比較は非常に潜行的です。