C# 一貫性のない比較


翻訳者から:
これは、以前は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を返しtrue9番目のケースでは、パック変数の型はshortであるため、型のチェック( z is int )は失敗し、メソッドはfalseを返しfalse

結果
2つの変数を比較するための9つの異なる方法を示しましたが、すべての場合において、比較される変数は1に等しく、半分の場合にのみ比較がtrue返しtrue 。 これがおかしくて、すべてが混乱していると思うなら、あなたは正しいです! C#での比較は非常に潜行的です。

Source: https://habr.com/ru/post/J209412/


All Articles