c# - property - ¿Es posible convertir un IEnumerable en IOrderedEnumerable sin utilizar OrderBy?
order object list c# (3)
En pocas palabras, IOrderedEnumerable existe únicamente para proporcionar una estructura gramatical a los métodos OrderBy () / ThenBy (), lo que le impide intentar comenzar una cláusula de pedido con ThenBy (). proceso. No pretende ser un "marcador" que identifique la colección según lo ordenado, a menos que OrderBy () lo haya ordenado realmente. Entonces, la respuesta es que si se supone que el método de clasificación es nulo para indicar que el enumerable está en algún "orden predeterminado", debe especificar ese orden predeterminado (como lo hace su implementación actual). No es sincero afirmar que el enumerable está ordenado cuando en realidad no lo está, incluso si, al no especificar un SortingMethod, se infiere que es "ordenado por nada" y no le importa el orden real.
El "problema" inherente al tratar de simplemente marcar la colección según lo ordenado utilizando la interfaz es que hay más en el proceso que simplemente ordenar. Al ejecutar una cadena de método de ordenamiento, como myCollection.OrderBy().ThenBy().ThenByDescending()
, en realidad no está ordenando la colección con cada llamada; no todavía, de todos modos. En su lugar, está definiendo el comportamiento de una clase "iterador", denominada OrderedEnumerable, que utilizará las proyecciones y comparaciones que defina en la cadena para realizar la ordenación en el momento en que necesite un elemento ordenado real.
La respuesta de Servy, que indica que OrderBy (x => 1) es un noop y debe optimizarse a partir de proveedores de SQL, ignora la realidad de que esta llamada, hecha contra un Enumerable, todavía hará bastante trabajo, y que la mayoría de los proveedores de SQL en de hecho, no optimice este tipo de llamadas; OrderBy (x => 1), en la mayoría de los proveedores de Linq, producirá una consulta con una cláusula "ORDER BY 1", que no solo obliga al proveedor de SQL a realizar su propia clasificación, sino que realmente producirá un cambio en el orden, porque en T-SQL al menos "PEDIDO POR 1" significa ordenar por la primera columna de la lista de selección.
Supongamos que hay un método de extensión para ordenar un IQueryable en función de varios tipos de clasificación (es decir, clasificación por varias propiedades) designado por SortMethod
enum.
public static IOrderedEnumerable<AClass> OrderByX(this IQueryable<AClass> values,
SortMethod? sortMethod)
{
IOrderedEnumerable<AClass> queryRes = null;
switch (sortMethod)
{
case SortMethod.Method1:
queryRes = values.OrderBy(a => a.Property1);
break;
case SortMethod.Method2:
queryRes = values.OrderBy(a => a.Property2);
break;
case null:
queryRes = values.OrderBy(a => a.DefaultProperty);
break;
default:
queryRes = values.OrderBy(a => a.DefaultProperty);
break;
}
return queryRes;
}
En el caso en que sortMethod
es null
(es decir, donde se especifica que no me importa el orden de los valores), hay una forma de que en lugar de ordenar por una propiedad predeterminada, simplemente pase los valores de IEnumerator
como " ordenado "sin tener que realizar el tipo real?
Me gustaría la posibilidad de llamar a esta extensión y luego realizar algunos pedidos adicionales de ThenBy
.
Si devuelve siempre el mismo valor de índice, obtendrá un IOrderedEnumerable que conservará el orden de la lista original:
case null:
queryRes = values.OrderBy(a => 1);
break;
Por cierto, no creo que esto sea lo correcto. Obtendrá una colección que se supone ordenada, pero en realidad no lo es.
Todo lo que necesita hacer para el caso predeterminado es:
queryRes = values.OrderBy(a => 1);
Esto será efectivamente un tipo noop. Debido a que OrderBy realiza una clasificación estable, la orden original se mantendrá en el caso de que los objetos seleccionados sean iguales. Tenga en cuenta que, dado que esto es un IQueryable
y no un IEnumerable
, es posible que el proveedor de consultas no realice una clasificación estable. En ese caso, debe saber si es importante mantener el orden, o si es apropiado simplemente decir "No me importa en qué orden es el resultado, siempre que pueda llamar a ThenBy
sobre el resultado".
Otra opción, que le permite evitar el tipo real, es crear su propia implementación IOrderedEnumerable
:
public class NoopOrder<T> : IOrderedEnumerable<T>
{
private IQueryable<T> source;
public NoopOrder(IQueryable<T> source)
{
this.source = source;
}
public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
{
if (descending)
{
return source.OrderByDescending(keySelector, comparer);
}
else
{
return source.OrderBy(keySelector, comparer);
}
}
public IEnumerator<T> GetEnumerator()
{
return source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return source.GetEnumerator();
}
}
Con eso su consulta puede ser:
queryRes = new NoopOrder<AClass>(values);
Tenga en cuenta que la consecuencia de la clase anterior es que si hay una llamada a ThenBy
ThenBy
será efectivamente un tipo de nivel superior. En efecto, está convirtiendo el ThenBy
posterior en una llamada OrderBy
. (Esto no debería sorprender; ThenBy
llamará al método CreateOrderedEnumerable
, y allí este código llama a OrderBy
, básicamente convirtiendo ese ThenBy
en un OrderBy
. Desde un punto de vista de clasificación conceptual, esta es una manera de decir que "todos los los elementos en esta secuencia son iguales a los ojos de este tipo, pero si especifica que los objetos iguales deben estar separados por otra cosa, entonces hágalo.
Otra forma de pensar en un tipo "no op" es que ordena los elementos basados en el índice de la secuencia de entrada. Esto significa que los ítems no son todos "iguales", significa que la secuencia de entrada de orden será el orden final de la secuencia de salida, y dado que cada ítem en la secuencia de entrada es siempre más grande que el anterior, agregando "desempate" adicional "Las comparaciones no harán nada, por lo que cualquier ThenBy
subsiguiente a ThenBy
sentido. Si se desea este comportamiento, es aún más fácil de implementar que el anterior:
public class NoopOrder<T> : IOrderedEnumerable<T>
{
private IQueryable<T> source;
public NoopOrder(IQueryable<T> source)
{
this.source = source;
}
public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
{
return new NoopOrder<T>(source);
}
public IEnumerator<T> GetEnumerator()
{
return source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return source.GetEnumerator();
}
}