8

この次の例では、3 番目の評価は false を返し、すべて正常ですが、4 番目の例は true を返します。
これがどのように機能するかはよくわかりませんが、デフォルトでは、オブジェクトが等しいかどうかの 2 つの参照を比較し、両方が一意のものを指しているObject.Equalsと見なします。これは false を返す必要があります。これは 3 番目の例では行われますが、4 番目の例では行われません。 メソッドが文字列クラスでオーバーライドされているため、2 番目の例で true を返す理由がわかりましたが、4 番目の例では、この文字列をオブジェクトとしてキャストしています。 この場合、それは呼び出されませんか?ab
.Equals()
Object.Equals

static void Main()
{
    // Create two equal but distinct strings
    string a = new string(new char[] {'h', 'e', 'l', 'l', 'o'});
    string b = new string(new char[] {'h', 'e', 'l', 'l', 'o'});

    Console.WriteLine (a == b); // Returns true
    Console.WriteLine (a.Equals(b)); // Returns true

    // Now let's see what happens with the same tests but
    // with variables of type object
    object c = a;
    object d = b;

    Console.WriteLine (c == d); // Returns false
    Console.WriteLine (c.Equals(d)); // Returns true
}
4

3 に答える 3

10

手がかりは「デフォルトで」という言葉です。カスタム実装でstringオーバーライドします。はポリモーフィック メソッド ( / / など) であるobject.Equalsため、変数 (/式) の型が であっても、最も派生した実装が使用されます。object.Equalsvirtualoverrideobject

==ただし、ポリモーフィックではありません。使用される実装は、変数 (/式) の型に完全に依存します。この場合、既知の型はobjectであるため、使用可能な唯一の比較は参照の等価性です。

おそらくもっと簡潔に:

class Foo {
    public override string ToString() { return "hi"; }
}
//...
object obj = new Foo();
string s = obj.ToString(); // this is "hi"

これは同じ原則です。コンパイラが認識している型 (objectこの場合) に関係なく、仮想メソッドの最も派生したオーバーロードが使用されます。

于 2014-04-23T07:00:39.877 に答える
1

メソッドは仮想です。Equalsつまり、object型の参照で呼び出された場合でも、インスタンスの具象型の実装を呼び出すことになります。

生成されたILからa.Equals(b)

IL_003C:  ldloc.0     // a
IL_003D:  ldloc.1     // b
IL_003E:  callvirt    System.String.Equals

そしてc.Equals(d)なる

IL_0057:  ldloc.2     // c
IL_0058:  ldloc.3     // d
IL_0059:  callvirt    System.Object.Equals

したがって、どちらもそれぞれ文字列型とオブジェクト型の参照に対する仮想呼び出しであり、どちらもEquals参照ではなくインスタンス自体でメソッドを呼び出します。

一方、a==b静的メソッドの呼び出しになりますSystem.String.op_Equality

IL_002F:  ldloc.0     // a
IL_0030:  ldloc.1     // b
IL_0031:  call        System.String.op_Equality

ちょうどc==dなる

IL_004D:  ldloc.2     // c
IL_004E:  ldloc.3     // d
IL_004F:  ceq      

check-equal単純な参照チェックだけを行う IL 命令の呼び出しです。


次のようなコードを使用して、オーバーライドされた実装が呼び出されていることを確認することもできます。

class MyClass
{
  public override bool Equals(object obj)
  {
      Console.WriteLine("My Class Equals is called");
      return true;
  }
}

void Main()
{
   object a = new MyClass();
   object b = new MyClass();
   Console.WriteLine (a.Equals(b));
}

これは出力されます

My Class Equals is called
True
于 2014-04-23T07:00:29.847 に答える
0

Equals は仮想メソッドであるため、equals を実装するクラスは、何があっても元の equals を自動的にオーバーライドします。object.Equals を使用する場合は、 を使用する必要がありますobject.ReferenceEquals(a,b)

詳細については、仮想メソッドがどのように機能するかを調べてください。それに慣れている場合は、vtables が実際にどのように実装されているかを調べてください (コツをつかめば、実際には非常に簡単です)。

于 2014-04-23T06:59:49.760 に答える