2012-04-23 13 views
6

Todavía soy nuevo en los operadores de sobrecarga. Pensé que estaba haciendo un gran trabajo hasta que llegue a este problema. NullReferenceException se lanza en el operador! =. Supongo que está usándolo en el método CompareTo, pero no estoy del todo seguro. Si alguien puede señalarme en la dirección correcta, estaría muy agradecido.¿Cómo anular correctamente la igualdad?

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"); 
     } 
    } 
} 

Cuando comento los operadores de binaory, el programa funciona según lo previsto. Mi pregunta es ¿cómo puedo proteger mis operadores binarios de las referencias nulas para poder guardarlas para las comparaciones manuales? Gracias por su tiempo.

+1

Uno de los objetos 'Task' que está comparando con'! = 'Está establecido en' null'. – dasblinkenlight

+1

Right-o .. Debe suponer que 't1' y/o' t2' pueden ser nulos en todas esas funciones estáticas .. –

Respuesta

13

Ambas respuestas dadas hasta ahora son incorrectas. La respuesta aceptada es incorrecta porque recursivamente accidentalmente. La otra respuesta es incorrecta porque dice que null no es igual a nulo.

Sus implementaciones de los operadores son todas incorrectas; se requieren para manejar correctamente las entradas nulas.

Su implementación de GetHashCode está muy dañada; intentas poner un número de catorce dígitos en un formato que puede aceptar nueve dígitos. Simplemente llame a GetHashCode en la fecha; ¡No hay necesidad de pasar por este rigamarole de convertirlo en una cuerda y luego convertirlo en un número!

La forma correcta de escribir el código es usar object.ReferenceEquals para hacer comparaciones de referencia en lugar de usar los operadores == y !=; es demasiado fácil hacer una recursión accidental.

El patrón típico dice así:

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

Los otros operadores de comparación se dejan como ejercicio.

+0

+1 y gracias por la solución Creo que debería probar incluso los fragmentos más simples en el depurador antes de publicar ... – dasblinkenlight

+0

¡Lo tengo! Gracias maestro, esto soluciona mucho. Mi problema era que estaba usando mi lógica para probar la comparación en lugar de tratar de usar el objeto. ReferenceEquals. Gracias por su paciencia y sus respuestas. – Powe8525

+0

Por si fuera poco, también se podría implementar 'IEtable '. Eso es más importante para los tipos de valores para evitar el boxeo, pero no duele hacerlo también con los tipos de referencia. – CodesInChaos

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

     } 
+3

Hmm, y si 't1' es nulo y' t2' no es nulo, no lo haría ¿Esperas que esto vuelva 'verdadero'? –

+0

Fue un ejemplo. La operación debe ser lo suficientemente inteligente como para definir su propia lógica. –

+0

También olvidó lanzar 't1' /' t2' a 'object' antes de comparar con' null'. Y arruinar el importante caso especial de 'null == null' no se justifica simplemente diciendo que este es un ejemplo. – CodesInChaos

3

Parece que uno de los Task objetos que se están comparando con != se establece en null. El operador integrado != compara las referencias y no se rompe, pero su operador intenta desreferenciar la tarea y se rompe.

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

Esta implementación devuelve false cuando ambas tareas son null. Debe implementar una comprobación nula simétrica en el operador ==.

+0

Gracias, intenté algo así solo para obtener un desbordamiento de pila, veo dónde me equivoqué. ¿Podría explicar qué es la verificación simétrica nula (el google parece confundido al respecto)? – Powe8525

+1

@ Powe8525 Cuando mencioné que el comportamiento de 'bool operator ==' debe ser simétrico con el de 'bool operator! =', Quise decir que debe tratar dos valores 'null' como iguales, y' 'null 'no debe ser igual a un valor no nulo. La implementación de '! =' De la respuesta se adhiere a estas reglas, por lo que la implementación de '==' debería seguirlas también. – dasblinkenlight

+1

@ Powe8525: Estás en lo cierto; el código dado aquí está roto porque se llama a sí mismo. El código correcto es comenzar con 'if (object.ReferenceEquals (t1, null)) {return! Object.ReferenceEquals (t2, null); } ' –