2017-03-15 4 views
-1

Ich habe eine Liste von tblDetail-Objekten. tblDetails haben txtTracked, eine Zeichenfolge. Ich möchte basierend auf dieser Eigenschaft sortieren. also tue ich:List.Sort, die behauptet, dass mein IComparer inkonsistent ist

switch (sortby) 
{ 
    //... 
    case "Tracked Y/N": 
     list.Sort((lhs, rhs) => { 
      return lhs.txtTracked.CompareTo(rhs.txtTracked); 
     }); 
     break; 
    case "Tracked Y/N-desc": 
     list.Sort((lhs, rhs) => { 
      return -lhs.txtTracked.CompareTo(rhs.txtTracked); 
     }); 
     break; 
    //... 
} 

außer manchmal txtTracked ist null. Ok, gut, ich auch:

switch (sortby) 
{ 
    //... 
    case "Tracked Y/N": 
     list.Sort((lhs, rhs) => { 
      string toCompare = lhs.txtTracked; 
      if (toCompare == null) 
       toCompare = ""; 
      return toCompare.CompareTo(rhs.txtTracked); 
     }); 
     break; 
    case "Tracked Y/N-desc": 
     list.Sort((lhs, rhs) => { 
      string toCompare = lhs.txtTracked; 
      if (toCompare == null) 
       toCompare = ""; 
      return -toCompare.CompareTo(rhs.txtTracked); 
     }); 
     break; 
    //... 
} 

Die regelmäßige aufsteigend funktioniert Art in Ordnung, aber aus irgendeinem Grunde, wenn ich dies mit der absteigenden Sortierung auf dem gleichen Satz von Daten, erhalte ich eine null-Verweisausnahme auf lhs in "Nachverfolgte Y/N-Desc". so ich mache:

switch (sortby) 
{ 
    //... 
    case "Tracked Y/N": 
     list.Sort((lhs, rhs) => { 
      string toCompare = lhs.txtTracked; 
      if (toCompare == null) 
       toCompare = ""; 
      return toCompare.CompareTo(rhs.txtTracked); 
     }); 
     break; 
    case "Tracked Y/N-desc": 
     //for some reason this sort adds null into the list. wat. 
     list.Sort((lhs, rhs) => { 
      if (lhs == null) 
       return 1; 
      if (rhs == null) 
       return -1; 
      string toCompare = lhs.txtTracked; 
      if (toCompare == null) 
       toCompare = ""; 
      return -toCompare.CompareTo(rhs.txtTracked); 
     }); 
     list.RemoveAll(d => d == null); 
     break; 
    //... 
} 

erst jetzt habe ich eine neue Fehlermeldung erhalten:

Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. 

was ist passiert? Es funktioniert gut in einer Richtung, aber nicht in der anderen. Wie entscheidet C#, dass meine Sortierfunktion nicht gut genug ist und wie kann ich das richtig machen?

und nebenbei, warum wird null in meine Liste hinzugefügt, wenn ich sort aufrufen?

+1

Wenn sowohl lhs als auch rhs null sind, sollte es 0 und nicht 1 wie jetzt zurückgeben. – Evk

+2

Das Negieren des Ergebnisses von 'CompareTo' ist keine zuverlässige Möglichkeit, eine Sortierung umzukehren; Es behandelt "int.MinValue" nicht korrekt. Sie sollten die Reihenfolge der Operanden umkehren, um die Sortierung umzukehren. – Servy

Antwort

1

Bisher war es ein Glück, dass die aufsteigende Sortierung keine Null-Referenz-Ausnahme ausgelöst hat. Oder sollte es so sein, haben Sie Pech gehabt, dass es Ihnen nicht aufgefallen ist.

Eines (oder mehrere) Ihrer Objekte ist null. Es ist einfach passiert, dass es auf der rechten Seite aufgetaucht ist, wenn Sie aufsteigende Sortierung verwendet haben, und auf absteigender Sortierung. Es ist in Ordnung, rhs == null zu haben, da die CompareTo-Methode dafür sorgt. Aber wenn lhs == null, erhalten Sie eine Ausnahme.

Es gibt bereits eine Null-Safe-Methode, die entweder/lhs und rhs null zulässt: die statische String.Compare(String, String). Sie können diese Methode verwenden, zusammen mit dem null-safe navigation operator ?. wie folgt:

list.Sort((lhs, rhs) => { return String.Compare(lhs?.txtTracked, rhs?.txtTracked); }); 

für die aufsteigende sortieren und

list.Sort((lhs, rhs) => { return String.Compare(rhs?.txtTracked, lhs?.txtTracked); }); 

für die absteigende Sortierung.

+1

@Adam: Beachten Sie, dass das obige auch vermeidet [die falsche Verwendung der Negation] (https://stackoverflow.com/questions/42820101/list-sort-alleging-that-my-icomparer-is-inconsistent#comment72752439_42820101) umzukehren die Sortierreihenfolge. –

+1

Aber mit diesem Ansatz, mit dem Operator '? .', erhalten wir alle Einträge, bei denen die' tblDetail' selbst 'null' ist, unter all jenen Einträgen gemischt, wo die' tblDetail' eine "gute" Instanz ist (dh nicht null), das zufällig seinen '.txtTracked' Member gleich' null' hat. –

+0

@ JeppeStigNielsen guter Punkt. Das OP muss eine Entscheidung darüber treffen, wie ein Null-lhs mit einem Null-rhs.txtTracked verglichen werden soll (wenn rhs nicht null ist). In dieser Antwort werden sie als gleich behandelt. Ob das gut ist oder nicht, hängt von der Problemdomäne ab. –

-1

Versuchen Sie, Variablen für die Beschreibung zu wechseln.

+0

Dies explodiert immer noch, wenn "rhs" selbst (oder "lhs" selbst) "null" ist. Also ich denke nicht, dass es die Frage beantwortet. Selbst wenn Ihr Code nicht explodiert, sortiert er alle Einträge, bei denen "txtTracked" "null" ist, zusammen mit denen, bei denen "txtTracked" ist "" ", alle gemischt. Das ist vielleicht nicht das, was wir wollen. 'null' und' '' 'werden normalerweise nicht als gleich angesehen. –

1

Sie:

list.Sort((lhs, rhs) => { 
     if (lhs == null) 
      return 1; 
     if (rhs == null) 
      return -1; 
     // more cases 
    }); 

Dies wird da nicht funktionieren, wenn beide Komparanden null sind, wird es immer positiv zurück. Der Sortieralgorithmus kann es also nie richtig machen. Sobald es zwei null tauscht, weil sie in der falschen Reihenfolge sind, sind sie zum Beispiel immer noch in der falschen Reihenfolge.

Was auch immer kommen Sie mit, wenn x-y Vergleich nicht Null ist, muss sie im Zeichen der y im Vergleich zu x entgegengesetzt sein.

Stattdessen könnten Sie tun:

list.Sort((lhs, rhs) => { 
     if (lhs == null || rhs == null) 
      return Comparer<object>.Default.Compare(rhs, lhs); 
     return string.Compare(rhs.txtTracked, lhs.txtTracked); 
    }); 

In dem Fall, dass eine oder beide null ist, ich sie als Objekte einfach vergleichen. Das wird funktionieren. Man könnte es auf eine andere Weise schreiben auch:

list.Sort((lhs, rhs) => { 
     if (lhs == null || rhs == null) 
      return (rhs != null).CompareTo(lhs != null); 
     return string.Compare(rhs.txtTracked, lhs.txtTracked); 
    }); 

Auf diese Weise verwendet, um die Tatsache, dass false kleiner als true.

Verwandte Themen