¿La interfaz IComparable está desactualizada / “dañina”?

11

IComparable solo funciona de una manera

Digamos que tienes una Employeeclase. En una vista, desea mostrar todo Employeesordenado por nombre, en otra, por dirección. ¿Cómo vas a lograr eso? No con IComparable, al menos no de ninguna manera idiomática.

IComparable tiene la lógica en el lugar equivocado

La interfaz se usa llamando .Sort(). En una vista que muestra Customerordenado por nombre, no hay ningún código que implique cómo se va a ordenar.
Por otro lado, la Customerclase está asumiendo cómo se va a usar, en este caso, que se usará en una lista ordenada por nombres.

IComparable se usa implícitamente

En comparación con las alternativas, es muy difícil ver dónde se está utilizando la lógica de comparación, o si es que lo está. Asumiendo su IDE estándar y comenzando desde la Customerclase, tendré que

  1. Buscar todas las referencias a Customer
  2. Encuentra las referencias que se usan en una lista
  3. Verifique si esas listas las han .Sort()llamado alguna vez

Lo que probablemente sea peor, si elimina una IComparableimplementación que todavía se está utilizando, no recibirá ningún error o advertencia. Lo único que obtendrá es un comportamiento incorrecto en todos los lugares que eran demasiado oscuros para que usted piense.

Estos problemas combinados, más requisitos cambiantes

La razón por la que llegué a pensar en esto es porque me salió mal. He estado felizmente usando IComparableen mi aplicación durante 2 años. Ahora, los requisitos cambiaron y la cosa necesita ser ordenada de 2 maneras diferentes. Se ha dado cuenta de que no es divertido seguir los pasos descritos en la sección anterior.

La pregunta

Estos problemas me hacen pensar que soy IComparableinferior IComparero .OrderBy(), hasta el punto de no ver ningún caso de uso válido que las alternativas no sirvan mejor.
¿Siempre es mejor usar IComparero LINQ, o hay ventajas / casos de uso que no estoy viendo aquí?

R. Schmitz
fuente
2
Su nuevo requisito de "ordenar dos formas diferentes" es un arenque rojo. Para resolverlo, todo lo que tiene que hacer es pasar un comparador diferente a su función de clasificación.
Robert Harvey
@RobertHarvey Entonces ya no usarías IComparablemás, lo que está reforzando mi punto.
R. Schmitz
No olvide que si usa las SortedXXXcolecciones, requieren que los elementos almacenados sean IComparableo que se IComparerproporcionen. También tenga en cuenta que es trivial invertir el orden de clasificación natural con un comparador y hacer que funcione con todos los IComparableobjetos.
Berin Loritsch
2
No importa que haya dos interfaces diferentes. IComparablese considera el mecanismo de comparación predeterminado . IComparerse usa cuando desea anular el mecanismo de comparación predeterminado.
Robert Harvey
Ejemplo ReverseComparer<T>: gist.github.com/jackfarrington/078e7af7bc82482aa634
Berin Loritsch

Respuestas:

14

IComparabletiene las restricciones que mencionaste, eso es correcto. Es una interfaz que ya estaba disponible en .NET Framework 1.0, donde esas alternativas funcionales y Linq no estaban disponibles. Entonces, sí, uno podría verlo como un elemento de marco obsoleto que se mantiene principalmente para la compatibilidad con versiones anteriores.

Sin embargo, para muchas estructuras de datos simples, una forma de clasificación es probablemente suficiente o natural. Para estos casos, tener un lugar canónico para implementar la relación de pedido sigue siendo una buena manera de mantener el código SECO, en lugar de repetir siempre la misma lógica en cada llamada a OrderBytodo el lugar.

Usted ha estado "felizmente usando IComparable en su aplicación durante 2 años", como escribió, por lo que me parece que le sirvió durante mucho tiempo. Cuando ahora tiene que validar, cambiar y probar todas las llamadas Sort, también puede ser una señal de que estaba haciendo el mismo tipo de lógica de clasificación en muchos lugares, lo cual no es culpa de IComparable. Entonces, esta podría ser una ocasión para centralizar más de esta lógica en un solo lugar, haciendo que su código sea más SECO.

Doc Brown
fuente
Buen punto sobre las estructuras de datos simples. Sin embargo, el último párrafo no tiene 100% de sentido para mí. Si no lo hubiera usado IComparable, todo el código de clasificación preexistente se habría dejado intacto en sus respectivas vistas, mientras que solo agregaría un nuevo código de clasificación para la nueva vista.
R. Schmitz
@ R.Schmitz ¿La ordenación preexistente habría funcionado correctamente sin la IComparableimplementación que escribió?
Robert Harvey
3
@ R.Schmitz: Claro, pero ahora estás comprometido a proporcionar siempre un comparador (a menos que uses OrderBy, por supuesto). Con IComparable, obtienes una implementación predeterminada de forma gratuita, y a veces ni siquiera tienes que escribir esa implementación.
Robert Harvey
2
@ R.Schmitz: Su último comentario allí resume muy bien el punto. Aunque iría un poco más lejos. Supongamos que tiene un tipo numérico como BigInteger. Si no implementara operadores / interfaces de comparación, ¿cómo implementaría usted mismo un IComparer ? Necesitaría acceso a las estructuras de datos internas para hacerlo de manera eficiente, o para nada. Supongamos que tiene un tipo como Cliente; las propiedades públicas que desea ordenar en tareas tienen comparadores. Para mí esa es la diferencia: implementar IComparable<T>si no sería razonable esperar que la persona que llama implemente un comparador.
Eric Lippert
1
If I hadn't used IComparable, all the pre-existing sorting code would have been left untouched in their respective views, while I'd only add new sorting code for the new view.Solo porque IComparableera una mejor solución en ese momento , no significa que sea la mejor solución hoy . Su primer comentario aquí implica que "es IComparable o nada", lo cual no es cierto, el problema podría haberse resuelto de muchas maneras diferentes. Las aplicaciones pueden crecer en tamaño / escala, y las cosas que solían verse adecuadas pueden no ser capaces de satisfacer las crecientes demandas de la aplicación.
Flater
1

Estoy de acuerdo con tus sentimientos sobre IComparable

Solo mira los comentarios sobre Array.Sort()

  • Cada elemento de la matriz debe implementar la IComparableinterfaz para poder realizar comparaciones con cualquier otro elemento de la matriz. (o se lanza una excepción)
  • Si la ordenación no se completa con éxito, los resultados no están definidos.

Probablemente nunca tengamos la motivación, ¡Sin embargo! considere object.Equals()un método en cada objeto que le permite comparar objetos entre sí para ver si son "iguales"

Ya lo tiene allí, pero se le ha encomendado la tarea Array.Sort()de agregar.object.Compare(object)

Ewan
fuente