2013-08-14 12 views
7

Sono un po 'confuso sull'uguaglianza dei contenuti nei tipi di riferimento in particolare. In entrambi i casi, io non prevale sull'uguaglianza, quindi perché il comportamento è diverso.Uguaglianza nei tipi di riferimento

Vedi 2 semplici esempi di codice:

Esempio 1: restituisce True

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

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

Esempio 2: Entrambe le affermazioni return false

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

Vai a questa qustion simili: http://stackoverflow.com/questions/2655151/bool-as-object-vs- string-as-object-testing-ugality – Oscar

risposta

8

ci sono due effetti sul lavoro qui:

  • String internato significa che in realtà anche se si esegue un controllo di uguaglianza di riferimento, ci si può comunque vedere True.È possibile risolvere che in questo modo:

    object o1 = new StringBuilder("ABC").ToString(); 
    object o2 = new StringBuilder("ABC").ToString(); 
    
  • System.Stringoverrides the Equals method per confrontare i contenuti delle stringhe:

    Questo metodo esegue un numero ordinale (e della cultura-insensitive maiuscole e minuscole) confronto.

    Si può vedere la differenza qui:

    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 
    

La classe non lo fa esclusione Equals, in modo che stai ricevendo il default implementation in Object, che è quello di confrontare i riferimenti:

Se l'istanza corrente è un tipo di riferimento, il metodo Equals (Object) verifica l'uguaglianza di riferimento, e una chiamata al metodo Equals (Object) equivale a una chiamata al metodo ReferenceEquals.

Si potrebbe rimediare ragionevolmente facilmente sovrascrivendo Equals:

// 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(); 
    } 
} 

Si noti che nel Equals implementazione avremmo potuto utilizzare:

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

... perché stringanche sovraccarichi l'operatore ==. Ma è diverso :)

+0

+1 Non avevo mai pensato di poter usare direttamente 'ReferenceEquals()' senza scrivere 'object.ReferenceEquals()'. È logico, considerando che ogni riga di codice in C# deve essere in un metodo di una classe ... – xanatos

+0

Una cosa di cui essere a conoscenza se si stanno implementando i propri metodi Equals, cose che controllano Equals prima di archiviare in una raccolta (come un HashSet o la chiave di un dizionario) si aspetta che l'output di 'GetHashCode' e' Equals' restituisca lo stesso output per un dato input per l'intera vita di un oggetto nella raccolta. –

+0

@ScottChamberlain: Ecco perché ho creato 'personName' di sola lettura :) Ci sono un sacco di altre cose da imparare sull'uguaglianza, ma non volevo entrare troppo qui. –

0

Nel primo caso, String.Equals override Object.Equals, e controlla i valori della stringa effettiva. L'uguaglianza di riferimento non viene utilizzata. Nel secondo caso, viene utilizzata l'uguaglianza di riferimento predefinita.

Tuttavia, se è stato utilizzato object.ReferenceEquals, si vedrebbe lo stesso comportamento a causa di string interning, che fa sì che i primi due oggetti per puntare alla stessa stringa in memoria (lo stesso riferimento), dal momento che si sta utilizzando stringhe letterali.

+0

No, nel primo caso chiama (via polymorphism) 'String.Equals'. Avresti lo stesso effetto * senza * l'internamento. –

+0

@JonSkeet Sì, stamattina stupida;) –

1

L'operatore di uguale su una stringa viene sovrascritto per confrontare la stringa byte per byte. Nel tuo secondo esempio, stai confrontando le istanze, cioè gli indirizzi di memoria, che sono diversi per le due istanze di Person()

3

L'esempio 1 restituisce true, perché Equals sta confrontando i valori, perché l'oggetto è trattato come una stringa ; mentre l'Esempio 2 sta confrontando le istanze degli oggetti e dato che ciascuna punta a diversi pezzi di memoria non sono uguali.

2

Per riferimento predefinito Equals per default su ReferenceEquals per i tipi di riferimento. Devi istanze di Person, quindi restituisce false.

String sostituisce Equals per ottenere semantica del valore. Pertanto, se si confrontano due istanze separate di string con lo stesso valore utilizzando Equals, viene restituito true.

Grazie alla stringa interna, sia lo "ABC" punto la stessa istanza. Quindi anche ReferenceEquals restituirebbe true nel tuo primo esempio.

Il tipo in fase di compilazione non è rilevante per Equals poiché è un metodo virtuale. Quindi il tuo cast a object non ha alcun effetto. Casting to object influenza solo == e != poiché sono sovraccaricati, non sovrascritti.

+0

# 2 è probabile ma non garantito. (Penso) – briantyler

+0

@TheMouthofaCow AFAIR è garantito all'interno di un assembly, ma non tra gli assembly. – CodesInChaos

0

String ha sovrascritto il suo metodo Equals in modo che se due stringhe contengono esattamente gli stessi caratteri nello stesso ordine, allora sono Equal. Il tuo Person non ha tale override così eredita dall'oggetto che usa il riferimento equivale a essere il suo arbitro di uguaglianza.

0

String riferimento pari a causa della stringa di internato in .net (si google su di esso)

Problemi correlati