2010-05-16 12 views
24

¿Para qué sirve la clase Comparer<T> si el tipo que especifica ya implementa IComparable?¿Para qué es la clase Comparer <T>?

Si especifico Comparer.Default, y el Cliente ya implementa IComparable, ¿por qué debería usar la clase Comparer?

+0

¿Qué estás preguntando exactamente?¿Estás preguntando por qué tener una interfaz tan similar a una clase abstracta? Si es así, "el tipo que especifique" es confuso ... – Kobi

+0

Psico: en el escenario que describe, no necesita 'Comparer '. Sin embargo, 'Customer' no puede implementar' IComparable', no tiene por qué. – Kobi

Respuesta

18

Creo que su pregunta es por qué tiene una clase base que solo parece tener un método útil que resulta ser el mismo método que implementaría si implementara la interfaz directamente. Si lo entendí correctamente, creo que tiene razón en que no se obtienen muchos beneficios derivando de Comparer<T> en lugar de implementar IComparer<T> directamente, excepto que la clase base le proporciona una implementación genérica y no genérica con un único método reemplazado.

Pero si su pregunta es ¿por qué tienen tanto IComparer<T> y IComparable<T>, entonces como otros han indicado, Comparer<T> le permite definir diferentes formas de realizar la comparación. Es una implementación del Strategy design pattern. Un gran ejemplo de esto serían las diversas propiedades StringComparer tales como StringComparer.Ordinal, StringComparer.OrdinalIgnoreCase, etc. Esto le permite ordenar las cadenas de forma diferente bajo circunstancias diferentes que la interfaz IComparable<T> simplemente no puede anticipar.

Pero además de poder redefinir la forma en que se realiza una comparación, a veces es la única forma de que pueda proporcionar un comparador externo. Por ejemplo, la clase ListView de Windows Forms le permite especificar un IComparer para su lógica de clasificación. Pero ListViewItem no implementa IComparable. Entonces, sin una estrategia de comparación que sepa cómo hacerlo, ListViewItem no se puede ordenar porque no tiene una implementación predeterminada de IComparable.

Así que al final del día, es sólo otro punto de extensibilidad que le permite más flexibilidad a la hora que no eres el autor del tipo que va a ser ordenados (o necesita múltiples estrategias de clasificación.)


EDITAR: En respuesta a su comentario a continuación: "si tengo una clase que implementa IComparer (mi propia clase), esto me permitiría ordenar en cualquier número de propiedades (una clasificación personalizada), ¿por qué entonces me molestaría en usar Comparer.Default "

Quizás un ejemplo ayude. Digamos que está escribiendo un método de extensión que verifica si un valor determinado se encuentra entre un rango.

public static bool Between<T>(this T value, T minValue, T maxValue) { 

    var comparer = Comparer<T>.Default; 

    int c1 = comparer.Compare(value, minValue); 
    int c2 = comparer.Compare(value, maxValue); 

    return (c1 >= 0 && c2 <= 0); 

} 

En este caso, no sé nada sobre el tipo T. Se puede aplicar IComparable o puede aplicar IComparable<T> o puede aplicar ninguna y una excepción será lanzada. Esto también me permite agregar fácilmente una sobrecarga para este método que permite que la persona que llama pase en su propio comparador. Pero Comparer es útil aquí porque me permite obtener un comparador predeterminado para un tipo desconocido que puede o no implementar una interfaz IComparable genérica o no genérica.

+0

Agregué un ejemplo a mi respuesta, espero que esto ayude. – Josh

+0

Gracias Josh, el ejemplo me ayudó a entender. Sin embargo, una pregunta, dijiste "El comparador predeterminado para un tipo desconocido que puede o no implementar una interfaz IComparable genérica o no genérica". Sé que el Comparador arroja una excepción si una clase no implica IComparable, entonces, ¿no querría siempre poner una restricción sobre T para implementar esta interfaz? –

+0

Bueno, normalmente, sí, pero porque IComparable (de T) no se deriva de IComparable, no se puede especificar realmente una restricción genérica que requiera la implementación genérica O no genérica. – Josh

9

El tipo no tiene que poner en práctica IComparable, que puede ser cualquier tipo - no hay restricciones sobre T:

public abstract class Comparer<T> : IComparer, IComparer<T> 

El nuevo Comparer que crean instrumentos IComparer<T> y la no genérico IComparer, y puede usarse para comparaciones y clasificación de colecciones.

Está en lo correcto: si su tipo, Customer implementa IComparable, y no necesita otra comparación, Comparer no es útil para usted. La mayoría de las clases en el marco .net pueden aceptar tanto IComparable<T> como Comparer<T>, por lo que puede usar cualquiera de ellas.

Sin embargo, se equivoca al suponer que siempre es el caso. Es muy posible crear un Comparer para un tipo no comparable. Tenga en cuenta que la siguiente es no requiere:

public abstract class Comparer<T> : IComparer, IComparer<T> 
            where T : IComparable, IComparable<T> 

Suponga que tiene una clase simple, Person y desea ordenar una lista de Persons, la mejor opción para escribir un comparador:

public class Person 
{ 
    string Name { get; set; } 
} 
0

Comparer<T> tiene un método de comparación real. Se puede usar si desea comparar el objeto de forma diferente que en su implementación IComparable.

22

Debido a que en ocasiones necesita mantener conjuntos/colas ordenadas por otra cosa, entonces el orden 'natural' o más, existe un orden natural.

Por ejemplo, si tiene líneas de plano es posible que desee ordenar por:

  • Número de vuelo
  • Destino
  • Tiempo
  • Prioridad (algunos vuelos pueden sufrir retrasos más largos y finalmente otros)
  • ...

tareas en la computadora puede ser programado por:

  • usuario
  • Prioridad (planificador)
  • PID (Comparación normal)
  • ...

Así que incluso en una aplicación que puede ser necesario para ordenar la objetos por diferentes propiedades. No puede hacer esto por el método int compareTo(Object) ya que no puede diferenciar entre contextos.Sin embargo, puede agregar el contexto, es decir, implementar CompareByPriority.

8

Hay algunos puntos sutiles aquí:

  • que no sólo apoyan IComparable<T> - que también es compatible con la más antigua (no genérico) IComparable como punto de retorno.Esto significa que puede no expresarse simplemente como (por ejemplo) una restricción genérico
  • apoya Nullable<T> donde T es comparable, aunque Nullable<T> claramente no esIComparable o IComparable<T>
  • impide explosión de restricciones de tipo genérico por no exigiéndolas - por ejemplo, un List<T> puede proporcionar un Sort aunque no insista en que todos T son ordenables; usted sería sorprendido la rapidez con las limitaciones genéricas otro modo se acumularía
  • que le permite pasar de un comparador en cualquiera de las API existentes que exigen un comparador, cuando todo lo que tienes es un tipo que puede ser comparable; la mayor parte de las API marco de clasificación (incluyendo LINQ) ofrecerá soporte comparador
2
public class Person 
{ 
    public string LastName; 
    public string FirstName; 

} 

public class Class2 
{ 
    public void test() 
    { 
     List<Person> classList = new List<Person>(); 
     //add some data to the list 
     PersonComparer comp = new PersonComparer(); 
     classList.Sort(comp); 
    } 
} 

public class PersonComparer : Comparer<Person> 
{ 

    public override int Compare(Person x, Person y) 
    { 
     int val = x.LastName.CompareTo(y.LastName); 
     if (val == 0) 
     { 
      val = x.FirstName.CompareTo(y.FirstName); 
     } 
     return val; 
    } 
} 
+2

El código habla por sí solo –

+0

Sí, pero en este caso, ¿por qué no usar IComparer ? –

+0

porque en este caso desea hacer una comparación especial. Primero quiere compararlos por apellido y luego por nombre. También podría poner una enumeración en su construtor que tomó una opción de comparación. Algo así como public Enum PersonSortOption {BirthDate, LastName, FirstName, Height, Wheight, HairColor} luego en su método de comparación use un interruptor para comparar/ordenarlos correctamente –

1

Si implementa un tipo IComparable<T>, es casi seguro que mejor usar eso que IComparable. Con los tipos de valor, el rendimiento de IComparable<T> es a menudo mucho mejor que el de IComparable no genérico. Con los tipos de referencia heredables, IComparable<T> puede ofrecer una mejor semántica que IComparable permitiendo clasificaciones basadas en campos de tipo derivado.

Para ver un ejemplo de este último beneficio, supongamos que uno tiene una clase base abstracta ScheduleEvent, con una propiedad EventTime, que implementa IComparable<ScheduleEvent> clasificando EventTime. Los tipos derivados incluyen ScheduledPopupMessageEvent con una cadena de mensaje, ScheduledGongEvent con un parámetro GongVolume. Múltiples ScheduleEvent s con el mismo EventTime deben informar cero para IComparable<ScheduleEvent>.CompareTo, porque no existe una manera segura y consistente de clasificar ScheduleEvent s de diferentes tipos, y porque dos ScheduleEvent s que se informan como no clasificados en relación con un tercero deben, para coherencia, informar ellos mismos como no relacionados entre sí. Por otro lado, no habría ningún problema con tener ScheduledGongEvent implementando IComparable<ScheduledGongEvent> teniendo en cuenta GongVolume en cuenta así como EventTime, o con ScheduledPopupMessageEvent haciendo lo mismo con su parámetro Mensaje.

Es útil, entonces, tener cosas como la clasificación rutinas utilizan IComparable<T> si es que existe, pero ser capaz de caer de nuevo a IComparable si IComparable<T> no existe. Sin embargo, verificar si una clase implementa IComparable<T> y seleccionar una implementación adecuada si es así es un poco costoso. Afortunadamente, una vez que se determina que un tipo tiene una implementación de IComparable<T>, se puede confiar en que siempre tendrá uno; Del mismo modo, si se encuentra un tipo que no tiene dicha implementación, nunca lo hará. Además, si una clase genérica tiene campos estáticos, cada combinación de parámetros de tipo producirá una clase diferente con campos diferentes. Por lo tanto, la primera vez que se ejecute Comparer<T>.Default con un parámetro de tipo particular T, almacenará la rutina Comparer que devuelve en un campo estático. Si Comparer<T> se ejecuta nuevamente con ese mismo tipo, devolverá la misma rutina del comparador. Aunque podría parecer extraño tener una clase estática Comparer<T> con un solo método, la creación de una clase Comparer<T> por separado para cada tipo T proporciona un lugar para almacenar la rutina de comparación creada.

Cuestiones relacionadas