c# - menos - IComparable e IComparable<T>
strcmp en c# (3)
Mientras que IEquatable <T> generalmente no debería ser implementado por clases no selladas, ya que tal derivación jugaría de manera extraña con la herencia a menos que la implementación simplemente llame a Object.Equals (en cuyo caso sería inútil), la situación opuesta surge con el genérico IComparable <T >. La semántica para Object.Equals e IEquatable <T> implica que siempre que se defina IEquatable <T>, su comportamiento debe reflejar el de Object.Equals (aparte de ser posiblemente más rápido y evitar el boxeo). Dos objetos de tipo DerivedFoo que se comparan como iguales cuando se consideran como tipo DerivedFoo también deben compararse como iguales cuando se consideran objetos de tipo Foo, y viceversa. Por otro lado, es completamente posible que dos objetos del tipo DerivedFoo que se clasifiquen de forma desigual cuando se consideren como el tipo DerivedFoo se clasifiquen por igual cuando se consideren del tipo Foo. La única forma de asegurar esto es usar IComparable <T>.
Supongamos, por ejemplo, que uno tiene una clase SchedulerEvent que contiene los campos ScheduledTime (de tipo DateTime) y ScheduledAction (de tipo MethodInvoker). La clase incluye los subtipos SchedulerEventWithMessage (que agrega un campo de mensaje de tipo cadena) y SchedulerEventWithGong (que agrega un campo GongVolume de tipo Double). La clase SchedulerEvent tiene un orden natural, según ScheduledTime, pero es totalmente posible que los eventos que no están ordenados entre sí sean desiguales. Las clases SchedulerEventWithMessage y SchedulerEventWithGong también tienen ordenamientos naturales entre sí, pero no en comparación con los elementos de la clase SchedulerEvent.
Supongamos que uno tiene dos eventos SchedulerEventWithMessage programados X e Y para el mismo tiempo, pero X.Message es "aardvark" e Y.Message es "zymurgy". ((IComparable <SchedulerEvent>) X) .CompareTo (Y) debe informar cero (dado que los eventos tienen tiempos iguales) pero ((IComparable <SchedulerEventWithMessage>) X) .CompareTo (Y) debe devolver un número negativo (ya que "aardvark" tipo antes de "zymurgy"). Si la clase no se comportara de esa manera, sería difícil o imposible ordenar constantemente una lista que contenga una mezcla de objetos SchedulerEventWithMessage y SchedulerEventWithGong.
Incidentalmente, se podría argumentar que sería útil tener la semántica de IEquatable <T> comparar objetos solo en base a los miembros de tipo T, de modo que, por ejemplo, IEtabletable <SchedulerEvent> verifique ScheduledTime y ScheduledAction para la igualdad, pero incluso cuando se aplica a un SchedulerEventWithMessage o SchedulerEventWithGong no verificará las propiedades de Mensaje o GongVolume. De hecho, esas serían semánticas útiles para un método IEtabletable <T>, y yo preferiría esa semántica, pero para un problema: Comparer <T> .Default.GetHashCode (T) siempre llama a la misma función Object.GetHashCode () independientemente de tipo T. Esto limita mucho la capacidad de IEquatable <T> para variar su comportamiento con diferentes tipos T.
¿Debo implementar tanto IComparable
como el genérico IComparable<T>
? ¿Hay alguna limitación si solo implemento una de ellas?
Oded tiene razón en que debería implementar tanto porque hay colecciones y otras clases que dependen de una sola de las implementaciones.
Pero hay un truco allí: IComparable <T> no debería arrojar excepciones, mientras que IComparable debería. Al implementar IComparable <T> usted está a cargo de garantizar que todas las instancias de T puedan compararse entre sí. Esto también incluye null (trate a null como más pequeño que todas las instancias no nulas de T y estará bien).
Sin embargo, IComparable general acepta System.Object y no puede garantizar que todos los objetos concebibles sean comparables con las instancias de T. Por lo tanto, si obtiene una instancia no-T pasada a IComparable, simplemente lance la excepción System.ArgumentException. De lo contrario, direccione la llamada a la implementación de IComparable <T>.
Aquí está el ejemplo:
public class Piano : IComparable<Piano>, IComparable
{
public int CompareTo(Piano other) { ... }
...
public int CompareTo(object obj)
{
if (obj != null && !(obj is Piano))
throw new ArgumentException("Object must be of type Piano.");
return CompareTo(obj as Piano);
}
}
Este ejemplo es parte de un artículo mucho más largo que contiene un extenso análisis de los efectos secundarios que debe tener en cuenta al implementar IComparable <T>: Cómo implementar la interfaz IComparable <T> en clases base y derivadas
Sí, deberías implementar ambos.
Si implementa uno, cualquier código que dependa del otro fallará.
Hay muchos códigos que usan IComparable
o IComparable<T>
pero no ambos, por lo tanto, la implementación de ambos garantiza que su código funcione con dicho código.