2012-04-23 14 views
6

Nadal nie mam do czynienia z przeciążaniem operatorów. Myślałem, że wykonuję świetną robotę, dopóki nie dotknęłam tego problemu. Wyjątek NullReferenceException jest zgłaszany dla operatora! =. Zakładam, że używa go w metodzie CompareTo, ale nie jestem całkowicie pewien. Jeśli ktokolwiek może wskazać mi właściwy kierunek, byłbym bardzo wdzięczny.Jak poprawnie zastąpić równość?

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

Kiedy komentuję operatorów binaory program działa zgodnie z przeznaczeniem. Moje pytanie brzmi: jak mogę chronić moje operatory binarne przed zerowymi referencjami, więc mogę zachować je dla ręcznych porównań? Dziękuję za poświęcony czas.

+1

Jeden z obiektów 'Task' porównywany z'! = 'Ma wartość' null'. – dasblinkenlight

+1

Prawo-o .. Musisz założyć, że 't1' i/lub' t2' może mieć wartość zerową we wszystkich tych statycznych funkcjach. –

Odpowiedz

13

Obie udzielone do tej pory odpowiedzi są błędne. Przyjęta odpowiedź jest błędna, ponieważ przypadkowo się powtórzy. Inna odpowiedź jest błędna, ponieważ mówi, że wartość null nie jest równa zeru.

Twoje wdrożenia operatorów są błędne; są one wymagane, aby poprawnie obsługiwać wejścia zerowe.

Twoja implementacja GetHashCode jest głęboko zepsuta; próbujesz umieścić czternastocyfrową liczbę w formacie, który może przyjąć dziewięć cyfr. Po prostu wywołaj GetHashCode na datę; nie ma potrzeby, aby przejść przez ten rigamarole zamieniając go w ciąg, a następnie zmieniając go w liczbę!

Właściwym sposobem napisania kodu jest użycie object.ReferenceEquals do porównywania odniesień zamiast korzystania z operatorów == i !=; jest zbyt łatwo zrobić przypadkową rekursję.

Typowy wzór wygląda tak:

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

Pozostałe operatory porównania są pozostawione jako ćwiczenie.

+0

+1 i dzięki za poprawkę! Sądzę, że powinienem spróbować nawet najprostszych fragmentów debuggera przed opublikowaniem ... – dasblinkenlight

+0

Rozumiem! Dziękuję Maestro, to naprawia tak wiele. mój problem polegał na używaniu mojej logiki do testowania porównania zamiast na próbie użycia obiektu.ReferenceEquals. Dziękuję za cierpliwość i odpowiedzi. – Powe8525

+0

Na dobrą sprawę można również zaimplementować 'IEquatable '. Jest to ważniejsze dla typów wartości, aby uniknąć boksowania, ale nie boli to również w przypadku typów referencyjnych. – CodesInChaos

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

     } 
+3

Hmm, a jeśli 't1' jest puste i' t2' nie jest puste, nie oczekujesz, że to zwróci "true"? –

+0

To był przykład. Operacja powinna być wystarczająco inteligentna, aby zdefiniować własną logikę. –

+0

Również zapomniałeś rzucić 't1' /' t2' na 'object' przed porównaniem z' null'. I zepsucie ważnego specjalnego przypadku 'null == null' nie jest usprawiedliwione po prostu mówiąc, że jest to przykład. – CodesInChaos

3

Wygląda jak jeden z Task obiektów, które są w porównaniu z != jest ustawiony na null. Wbudowany operator != porównuje odniesienia i nie pęka, ale twój operator próbuje dereferencji zadania i zrywa.

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

Ta implementacja zwraca false gdy oba zadania są null. Powinieneś zaimplementować symetryczne sprawdzanie wartości pustych w operatorze ==.

+0

Dziękuję, próbowałem czegoś takiego tylko po to, aby uzyskać przepełnienie stosu, widzę, gdzie poszedłem źle. Czy możesz wyjaśnić, co to jest symetryczne sprawdzanie wartości zerowych (Google wydaje się być zdezorientowany)? – Powe8525

+1

@ Powe8525 Kiedy wspomniałem, że zachowanie twojego operatora 'bool ==' musi być symetryczne z twoim 'bool operator! =', Miałem na myśli, że powinno traktować dwie wartości 'null' jako równe, i że' null 'nie powinno być równe wartości innej niż null. Implementacja '! =' Z odpowiedzi jest zgodna z tymi regułami, więc implementacja '==' również powinna ich przestrzegać. – dasblinkenlight

+1

@ Powe8525: Masz rację; podany tu kod jest zepsuty, ponieważ sam się nazywa. Poprawny kod rozpoczyna się od 'if (object.ReferenceEquals (t1, null)) {return! Object.ReferenceEquals (t2, null); } ' –