2013-08-14 9 views
7

Ich bin ein wenig verwirrt über Inhaltsgleichheit in Referenztypen speziell. Ich übertreibe die Gleichheit nicht in beiden Fällen - warum ist das Verhalten anders?Gleichheit in den Referenztypen

Siehe 2 einfache Code-Beispiele:

Beispiel 1: Gibt True zurück

class Program 
{ 
    static void Main(string[] args) 
    { 
     object o1 = "ABC"; 
     object o2 = "ABC"; 

     Console.WriteLine("object1 and object2: {0}", o1.Equals(o2)); 
    } 
} 

Beispiel 2: Die beiden Aussagen falsch

class Program 
{ 
    static void Main(string[] args) 
    { 
     Person person1 = new Person("John"); 
     Person person2 = new Person("John"); 

     Console.WriteLine("person1 and person2: {0}", person1.Equals(person2)); 
     Console.WriteLine("person1 and person2: {0}", ((object)person1).Equals((object)person2)); 

     Console.ReadLine(); 
    } 
} 

public class Person 
{ 
    private string personName; 

    public Person(string name) 
    { 
     this.personName = name; 
    } 
} 
+1

diese ähnliche qustion Siehe: http://stackoverflow.com/questions/2655151/bool-as-object-vs- string-as-object-testing-equality – Oscar

Antwort

8

Es gibt zwei Effekte bei der Arbeit hier:

  • String Internierung bedeutet, dass tatsächlich auch wenn Sie einen Verweis Gleichheitsprüfung durchführen, haben Sie immer True sehen werden.Sie können, dass wie dieses Problem zu beheben:

    Diese Methode führt eine Ordnungs (Groß- und Kleinschreibung kulturunabhängige) Vergleich:

    object o1 = new StringBuilder("ABC").ToString(); 
    object o2 = new StringBuilder("ABC").ToString(); 
    
  • System.Stringoverrides the Equals method den Inhalt der Saiten zu vergleichen.

    Sie können den Unterschied sehen Sie hier:

    object o1 = new StringBuilder("ABC").ToString(); 
    object o2 = new StringBuilder("ABC").ToString(); 
    Console.WriteLine(o1.Equals(o2)); // Prints True due to overriding 
    Console.WriteLine(ReferenceEquals(o1, o2)); // Prints False 
    

Ihre Klasse nicht Überschreibung Equals, so dass Sie die default implementation in Object bekommen, was Referenzen vergleichen:

Wenn die aktuelle Instanz ein Referenztyp ist, testet die Equals (Object) -Methode auf Referenzgleichheit. und ein Aufruf der Equals (Object) -Methode entspricht einem Aufruf der ReferenceEquals-Methode.

Sie konnten das beheben recht leicht durch Equals zwingende:

// It's easier to implement equality correctly on sealed classes 
public sealed class Person 
{ 
    private readonly string personName; 

    public Person(string name) 
    { 
     if (name == null) 
     { 
      throw new ArgumentNullException("name"); 
     } 
     this.personName = name; 
    } 

    public override bool Equals(object other) 
    { 
     Person person = other as Person; 
     return person != null && person.personName.Equals(personName); 
    } 

    // Must override GetHashCode at the same time... 
    public override int GetHashCode() 
    { 
     // Just delegate to the name here - it's the only thing we're 
     // using in the equality check 
     return personName.GetHashCode(); 
    } 
} 

Beachten Sie, dass in der Equals Implementierung wir verwendet haben könnte:

return person != null && person.personName == personName; 

... weil stringauch Überlastungen der Operator ==. Aber das ist eine andere Sache :)

+0

+1 Ich hatte nie gedacht, dass Sie 'ReferenceEquals()' direkt verwenden könnten, ohne 'object.ReferenceEquals()' zu schreiben. Es ist logisch, wenn man bedenkt, dass jede Codezeile in C# eine Methode einer Klasse sein muss ... – xanatos

+0

Eine Sache, die beachtet werden muss, wenn Sie eigene Equals-Methoden implementieren, die Equals vor dem Speichern in einer Sammlung überprüfen (wie ein HashSet oder der Schlüssel eines Dictionary) erwartet, dass die Ausgabe von 'GetHashCode' und' Equals' die gleiche Ausgabe für eine gegebene Eingabe für die gesamte Lebenszeit eines Objekts in der Sammlung zurückgibt. –

+0

@ScottChamberlain: Deshalb habe ich 'personName' nur zum Lesen gemacht :) Es gibt noch eine Menge anderer Sachen über Gleichheit zu lernen, aber ich wollte hier nicht zu viel machen. –

0

Im ersten Fall zurückkehren, String.Equals Überschreibungen Object.Equals und überprüft die tatsächlichen Zeichenfolgenwerte. Referenzgleichheit wird nicht verwendet. Im zweiten Fall wird die Standard-Referenzgleichheit verwendet. Wenn Sie jedoch 10 verwendet haben, sehen Sie das gleiche Verhalten aufgrund von string interning, wodurch die ersten beiden Objekte auf dieselbe Zeichenfolge im Speicher verweisen (die gleiche Referenz), da Sie Zeichenfolgenliterale verwenden.

+0

Nein, im ersten Fall ruft es (über Polymorphismus) 'String.Equals' auf. Sie würden den gleichen Effekt * ohne * Praktikum erzielen. –

+0

@JonSkeet Ja - dumm von mir heute Morgen;) –

1

Der Gleichheitsoperator in einer Zeichenfolge wird überschrieben, um die Zeichenfolge Byte für Byte zu vergleichen. In Ihrem zweiten Beispiel vergleichen Sie Instanzen, dh Speicheradressen, die für die beiden Instanzen von Person() unterschiedlich sind.

3

Beispiel 1 gibt true zurück, da Equals die Werte vergleicht, da das Objekt als Zeichenfolge behandelt wird ; Während Beispiel 2 die Instanzen der Objekte vergleicht und da sie jeweils auf unterschiedliche Speicherbereiche zeigen, sind sie nicht gleich.

2

Standardmäßig ist die Referenz Equals standardmäßig auf ReferenceEquals für Referenztypen eingestellt. Sie müssen Instanzen von Person, so dass es false zurückgibt.

String überschreibt Equals, um Wert Semantik zu erreichen. Wenn Sie also zwei separate Instanzen von string mit dem gleichen Wert unter Verwendung von Equals vergleichen, wird "true" zurückgegeben.

Dank String Interning sowohl "ABC" Punkt der gleichen Instanz. Also selbst ReferenceEquals würde true in Ihrem ersten Beispiel zurückgeben.

Der Typ der Kompilierzeit spielt keine Rolle, da es sich um eine virtuelle Methode handelt: Equals. So haben deine Umwandlungen auf object keinen Effekt. Casting to object beeinflusst nur == und !=, da sie überladen, nicht überschrieben werden.

+0

# 2 ist wahrscheinlich, aber nicht garantiert. (Ich denke) – briantyler

+0

@TheMouthofaCow AFAIR es ist innerhalb einer Baugruppe garantiert, aber nicht über Baugruppen. – CodesInChaos

0

String hat seine Equals Methode überschrieben, so dass, wenn zwei Zeichenketten enthalten genau die gleichen Zeichen in der gleichen Reihenfolge, dann sind sie die Equal. Ihr Person hat keine solche Überschreibung, so dass er vom Objekt erbt, das den Gleichheits-Arbitrierer als Referenz verwendet.

0

String Verweis wegen Zeichenfolge gleich in .net Internierung (werden Sie über Google es)