2012-04-23 14 views
6

Sono ancora nuovo per gli operatori che sovraccaricano. Ho pensato che stavo facendo un ottimo lavoro fino a quando non avrò colpito questo problema. NullReferenceException viene lanciata sull'operatore! =. Presumo che lo usi nel metodo CompareTo ma non ne sono del tutto sicuro. Se qualcuno può indicarmi la giusta direzione, sarei molto grato.Come sovrascrivere correttamente l'uguaglianza?

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 

namespace WindowsFormsApplication2 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
      List<Task> tasks = new List<Task>(); 
      tasks.Add(new Task("first", DateTime.Now.AddHours(2))); 
      tasks.Add(new Task("second", DateTime.Now.AddHours(4))); 
      tasks.TrimExcess(); 
      tasks.Sort(); 
     } 
    } 
    public class Task : IComparable 
    { 
     public Task() 
     { 
     } 
     public Task(string nameIn, DateTime dueIn) 
     { 
      nameOfTask = nameIn; 
      dateDue = dueIn; 
     } 
     DateTime dateDue; 
     string nameOfTask; 

     public static bool operator <(Task t1, Task t2) 
     { 
      return (t1.dateDue < t2.dateDue); 
     } 
     public static bool operator >(Task t1, Task t2) 
     { 
      return (t1.dateDue > t2.dateDue); 
     } 
     public static bool operator ==(Task t1, Task t2) 
     { 
      return (t1.dateDue == t2.dateDue); 
     } 
     public static bool operator !=(Task t1, Task t2) 
     { 
       return (t1.dateDue != t2.dateDue); 

     } 
     public override int GetHashCode() 
     { 
      return Int32.Parse(this.dateDue.ToString("yyyymmddhhmmss")); 
     } 
     public override bool Equals(System.Object obj) 
     { 
      if (obj == null) return false;  

      Task t = obj as Task; 
      if ((System.Object)t == null) return false; 
      return (this.dateDue == t.dateDue); 
     } 
     int IComparable.CompareTo(object obj) 
     { 
      if (obj == null) return 1; 

      Task t = obj as Task; 
      if (t != null) 
      { 
       return this.dateDue.CompareTo(t.dateDue); 
      } 
      else 
       throw new ArgumentException("Object is not a Task"); 
     } 
    } 
} 

Quando commento agli operatori binaory il programma funziona come previsto. La mia domanda è: come posso proteggere i miei operatori binari da riferimenti null, quindi posso tenerli per confronti manuali? Grazie per il tuo tempo.

+1

Uno degli oggetti 'Task' che si sta confrontando con'! = 'È impostato su' null'. – dasblinkenlight

+1

Destra-o .. È necessario assumere 't1' e/o' t2' può essere nullo in tutte quelle funzioni statiche. –

risposta

13

Entrambe le risposte fornite finora sono errate. La risposta accettata è errata perché ricorre casualmente. L'altra risposta è errata perché dice che null non è uguale a null.

Le implementazioni degli operatori sono tutte sbagliate; sono necessari per gestire correttamente gli input nulli.

L'implementazione di GetHashCode è profondamente interrotta; si tenta di inserire un numero di quattordici cifre in un formato che possa accettare nove cifre. Basta chiamare GetHashCode alla data; non c'è bisogno di passare attraverso questa rigamarole trasformandola in una stringa e trasformandola in un numero!

Il modo corretto di scrivere il codice è utilizzare object.ReferenceEquals per fare confronti di riferimento anziché utilizzare gli operatori == e !=; è fin troppo facile fare una ricorsione casuale.

Lo schema tipico è questa:

public static bool operator ==(Task t1, Task t2)   
{ 
    if (object.ReferenceEquals(t1, t2)) return true; 
    // All right. We know that they are (1) not the same object, and 
    // (2) not both null. Maybe one of them is null. 
    if (object.ReferenceEquals(t1, null)) return false; 
    if (object.ReferenceEquals(t2, null)) return false; 
    // They are not the same object and both are not null. 
    return t1.dateDue == t2.dateDue; 
} 

public static bool operator !=(Task t1, Task t2)   
{ 
    // Simply call the == operator and invert it. 
    return !(t1 == t2); 
} 

public override bool Equals(object t) 
{ 
    return (t as Task) == this; 
} 

public override int GetHashCode() 
{ 
    return this.dateDue.GetHashCode(); 
} 

Gli altri operatori di confronto sono lasciati come esercizio.

+0

+1 e grazie per la correzione! Credo che dovrei provare anche i frammenti più semplici nel debugger prima di postare ... – dasblinkenlight

+0

Ho capito! Grazie maestro, questo risolve così tanto. il mio problema era che stavo usando la mia logica per testare il confronto invece di provare a usare l'oggetto.ReferenceEquals. Grazie per la vostra pazienza e le vostre risposte. – Powe8525

+0

Per buona misura si potrebbe anche implementare 'IEquatable '. Questo è più importante per i tipi di valori per evitare il pugilato, ma non fa male fare anche con i tipi di riferimento. – CodesInChaos

-4
public static bool operator !=(Task t1, Task t2) 
     { 
       if (null == t1 || null == t2) { return false;} 
       return (t1.dateDue != t2.dateDue); 

     } 
+3

Hmm, e se 't1' è nullo e' t2' non è nullo, non sarebbe ti aspetti che questo restituisca 'true'? –

+0

Era un esempio. L'op dovrebbe essere abbastanza intelligente da definire la propria logica. –

+0

Hai anche dimenticato di lanciare 't1' /' t2' a 'object' prima di confrontarlo con' null'. E fare confusione nel caso speciale importante di 'null == null' non è giustificato semplicemente dicendo che questo è un esempio. – CodesInChaos

3

Sembra che uno dei Task oggetti che si stanno confrontando con != è impostato su null. L'operatore integrato != confronta i riferimenti e non si interrompe, ma l'operatore tenta di dereferenziare l'attività e interrompe.

public static bool operator !=(Task t1, Task t2) { 
    if (ReferenceEquals(t1, null)) { 
     return !ReferenceEquals(t2, null); // return true only if t2 is *not* null 
    } 
    if (ReferenceEquals(t2, null)) { 
     return true; // we know that t1 is not null 
    } 
    return (t1.dateDue != t2.dateDue); 
} 

Questa implementazione restituisce false quando entrambi i compiti sono null. È necessario implementare un controllo nullo simmetrico nell'operatore ==.

+0

Grazie, ho provato qualcosa del genere solo per ottenere uno stack overflow, vedo dove sono andato storto. Potresti spiegare cosa è il controllo nullo di Symmetric (il google sembra confuso al riguardo)? – Powe8525

+1

@ Powe8525 Quando ho detto che il comportamento del tuo operatore bool == 'deve essere simmetrico con quello del tuo operatore bool! =', Intendevo che avrebbe dovuto trattare due valori 'null' come uguali, e che' null 'non dovrebbe essere uguale a un valore non nullo. L'implementazione di '! =' Dalla risposta aderisce a queste regole, quindi anche l'implementazione di '==' dovrebbe seguirle. – dasblinkenlight

+1

@ Powe8525: sei corretto; il codice qui riportato è rotto perché si chiama da solo. Il codice corretto inizia con 'if (object.ReferenceEquals (t1, null)) {return! Object.ReferenceEquals (t2, null); } ' –

Problemi correlati