sort orderby ordenar how collection ascending array c# list sorting

orderby - order by collection c#



List.Sort en C#: comparador que se llama con un objeto nulo (7)

¿Estás seguro de que el problema no es que SomeProp sea null ?

En particular, con cadenas o Nullable<T> .

Con cuerdas, sería mejor usar:

list.Sort((x, y) => string.Compare(x.SomeProp, y.SomeProp));

(editar)

Para un contenedor seguro para nulos, puede usar Comparer<T>.Default : por ejemplo, para ordenar una lista por una propiedad:

using System; using System.Collections.Generic; public static class ListExt { public static void Sort<TSource, TValue>( this List<TSource> list, Func<TSource, TValue> selector) { if (list == null) throw new ArgumentNullException("list"); if (selector == null) throw new ArgumentNullException("selector"); var comparer = Comparer<TValue>.Default; list.Sort((x,y) => comparer.Compare(selector(x), selector(y))); } } class SomeType { public override string ToString() { return SomeProp; } public string SomeProp { get; set; } static void Main() { var list = new List<SomeType> { new SomeType { SomeProp = "def"}, new SomeType { SomeProp = null}, new SomeType { SomeProp = "abc"}, new SomeType { SomeProp = "ghi"}, }; list.Sort(x => x.SomeProp); list.ForEach(Console.WriteLine); } }

Estoy teniendo un comportamiento extraño al usar la función C # List.Sort integrada con un comparador personalizado.

Por alguna razón, a veces llama al método de comparación de la clase del comparador con un objeto nulo como uno de los parámetros. Pero si reviso la lista con el depurador, no hay objetos nulos en la colección.

Mi clase comparadora se ve así:

public class DelegateToComparer<T> : IComparer<T> { private readonly Func<T,T,int> _comparer; public int Compare(T x, T y) { return _comparer(x, y); } public DelegateToComparer(Func<T, T, int> comparer) { _comparer = comparer; } }

Esto permite que un delegado se pase al método List.Sort, como este:

mylist.Sort(new DelegateToComparer<MyClass>( (x, y) => { return x.SomeProp.CompareTo(y.SomeProp); });

Así que el delegado anterior lanzará una excepción de referencia nula para el parámetro x , aunque ningún elemento de mylist sea ​​nulo.

ACTUALIZACIÓN: Sí, estoy absolutamente seguro de que es el parámetro x que lanza la excepción de referencia nula.

ACTUALIZACIÓN: En lugar de usar el método List.Sort del marco, probé un método de orden personalizado (es decir, nuevo BubbleSort (). Sort (mylist) ) y el problema desapareció. Como sospechaba, el método List.Sort pasa nulo al comparador por alguna razón.


Este problema ocurrirá cuando la función de comparación no sea consistente, de modo que x <y no siempre implique y <x. En su ejemplo, debe verificar cómo se comparan dos instancias del tipo de SomeProp.

Aquí hay un ejemplo que reproduce el problema. Aquí, es causada por la función de comparación patológica "compareStrings". Depende del estado inicial de la lista: si cambia el orden inicial a "C", "B", "A", entonces no hay excepción.

No llamaría a esto un error en la función de clasificación, es simplemente un requisito de que la función de comparación sea consistente.

using System.Collections.Generic; class Program { static void Main() { var letters = new List<string>{"B","C","A"}; letters.Sort(CompareStrings); } private static int CompareStrings(string l, string r) { if (l == "B") return -1; return l.CompareTo(r); } }


La respuesta de Marc es útil. Estoy de acuerdo con él en que la NullReference se debe a llamar a CompareTo en una propiedad nula. Sin necesidad de una clase de extensión, puedes hacer:

mylist.Sort((x, y) => (Comparer<SomePropType>.Default.Compare(x.SomeProp, y.SomeProp)));

donde SomePropType es el tipo de SomeProp


Me encontré con este problema y descubrí que estaba relacionado con una propiedad de NaN en mi opinión. Aquí hay un caso de prueba mínimo que debería producir la excepción:

public class C { double v; public static void Main() { var test = new List<C> { new C { v = 0d }, new C { v = Double.NaN }, new C { v = 1d } }; test.Sort((d1, d2) => (int)(d1.v - d2.v)); } }


Para propósitos de depuración, usted quiere que su método sea nulo seguro. (o al menos, capture la excepción nula-ref., y manéjela de algún modo codificado). Luego, use el depurador para ver qué otros valores se comparan, en qué orden y qué llamadas tienen éxito o fallan.

Luego encontrará su respuesta y podrá eliminar la seguridad nula.


Puedes ejecutar este código ...

mylst.Sort((i, j) => { Debug.Assert(i.SomeProp != null && j.SomeProp != null); return i.SomeProp.CompareTo(j.SomeProp); } );


También me he encontrado con este problema (se pasó una referencia nula a mi implementación personalizada de IComparer) y finalmente descubrí que el problema se debía a la función de comparación inconsistente.

Esta fue mi implementación inicial de IComparer:

public class NumericStringComparer : IComparer<String> { public int Compare(string x, string y) { float xNumber, yNumber; if (!float.TryParse(x, out xNumber)) { return -1; } if (!float.TryParse(y, out yNumber)) { return -1; } if (xNumber == yNumber) { return 0; } else { return (xNumber > yNumber) ? 1 : -1; } } }

El error en este código fue que la comparación devolvería -1 siempre que uno de los valores no se pudiera analizar correctamente (en mi caso se debió a representaciones de cadena de valores numéricos con formato incorrecto, por lo que TryParse siempre falló).

Tenga en cuenta que, en caso de que tanto x como y se formatearon incorrectamente (y, por lo tanto, TryParse falló en ambos), al llamar a Comparar (x, y) y Comparar (y, x) se obtendría el mismo resultado: -1. Este creo que fue el principal problema. Al realizar la depuración, se pasaría el puntero de cadena nula a Compare () como uno de sus argumentos en algún momento, aunque la colección que estaba siendo clasificada no contuviera una cadena nula.

Tan pronto como solucioné el problema de TryParse y aseguré la coherencia de mi implementación, el problema desapareció y no se pasaron los punteros a la comparación.