example c# icomparable

c# - example - ¿Por qué tengo que sobrecargar a los operadores al implementar CompareTo?



icomparable c# (2)

Dos razones principales:

  1. Es una estructura general para todos los operadores. Si bien los operadores de comparación nunca pueden tener una semántica alternativa, hay una gran utilidad en una estructura que permite una semántica muy diferente para algunos de los otros operadores. Implementar una estructura separada solo para los operadores de comparación habría requerido omitir alguna otra característica, probablemente mucho más útil. Echa un vistazo a esta elegante implementación de BNF dentro de C # para ver un ejemplo.
  2. Las implementaciones predeterminadas, para el caso de los tipos de valor que lo tienen, se basan necesariamente en Reflexión, y por lo tanto son horriblemente ineficientes. Solo usted conoce la forma más eficiente de implementar estos operadores para sus clases. En muchos casos, no todos los campos de una estructura deben compararse con la igualdad de prueba, ni todos los campos necesitan combinarse en una implementación adecuada de GetHashCode. Ninguna implementación predeterminada puede determinar eso para todos los tipos, ya que es reducible al problema de detención.

Actualización según Eric Lippert, entre otros, la siguiente es la implementación estándar apropiada de los operadores de comparación en C # para un tipo UDT:

public int CompareTo(UDT x) { return CompareTo(this, x); } public bool Equals(UDT x) { return CompareTo(this, x) == 0; } public static bool operator < (UDT x, UDT y) { return CompareTo(x, y) < 0; } public static bool operator > (UDT x, UDT y) { return CompareTo(x, y) > 0; } public static bool operator <= (UDT x, UDT y) { return CompareTo(x, y) <= 0; } public static bool operator >= (UDT x, UDT y) { return CompareTo(x, y) >= 0; } public static bool operator == (UDT x, UDT y) { return CompareTo(x, y) == 0; } public static bool operator != (UDT x, UDT y) { return CompareTo(x, y) != 0; } public override bool Equals(object obj) { return (obj is UDT) && (CompareTo(this, (UDT)obj) == 0); }

Solo agregue la definición personalizada para private static int CompareTo(UDT x, UDT y) y mezcle.

Digamos que tengo un tipo que implementa IComparable.

Pensé que es razonable esperar que los operadores == ,! != , > , < , >= Y <= "simplemente funcionen" automáticamente llamando a CompareTo, pero en cambio tengo que anularlos a todos si quiero usarlos.

Desde la perspectiva del diseño del lenguaje, ¿hay alguna buena razón para hacerlo de esta manera? ¿Hay algún caso en el que sea realmente útil que A>B comporte de manera diferente para Compare(A,B)>0 ?


Toda la situación es desconcertante. C # tiene demasiadas formas de expresar igualdad y desigualdad:

  • los operadores ==! => <> = <= (que son métodos lógicamente estáticos)
  • El método estático Equals (que llama al método virtual), el método virtual Equals, el método ReferenceEquals
  • Las interfaces IComparable y IEquatable

Todos tienen una semántica sutilmente diferente y, con la excepción de los equivalentes estáticos, ninguno utiliza automáticamente el otro y ninguno tiene el comportamiento que deseo. Los métodos estáticos se envían según el tipo de tiempo de compilación de ambos operandos; los métodos virtuales / métodos de interfaz se distribuyen según el tipo de tiempo de ejecución de uno de los operandos, lo que hace que la operación sea asimétrica; el tipo de un lado importa más que el tipo del otro.

No puedo imaginar que alguien piense que la situación en la que estamos es genial; Sin restricciones, esto no es lo que habría evolucionado. Pero los diseñadores de lenguaje administrado tienen restricciones: el CLR no implementa métodos estáticos en contratos de interfaz o doble despacho virtual, o la capacidad de poner una restricción de operador en un parámetro de tipo genérico. Y, por lo tanto, múltiples soluciones han evolucionado para resolver el problema de igualdad / desigualdad.

Creo que si los diseñadores de CLR y C # retrocedieran en el tiempo y dijeran a sus seres pasados ​​qué características deberían estar en la versión 1 del CLR, alguna forma de métodos estáticos en las interfaces sería una prioridad en la lista. Si hubiera métodos estáticos en la interfaz, entonces podemos definir:

interface IComparable<in T, in U> { static bool operator <(T t, U u); static bool operator >(T t, U u); ... etc

Y luego si tienes:

static void Sort<T>(T[] array) where T : IComparable<T, T>

Luego puede usar los operadores < y == y así sucesivamente para comparar elementos.