2009-05-27 12 views
9

Cuando quiero a la restricción del tipo T para ser comparable, debería usar:restricción de interfaz para IComparable

where T : IComparable 

o

where T : IComparable<T> 

no puedo sacarlo de mi cabeza si # 2 marcas sentido. ¿Alguien puede explicar cuál sería la diferencia?

Respuesta

2

El IComparable<T> permite que el comparador sea fuertemente tipado.

Puede tener

public int CompareTo(MyType other) 
{ 
    // logic 
} 

como se oponen a

public int CompareTo(object other) 
{ 
    if (other is MyType) 
     // logic 
} 

Tomemos por ejemplo el siguiente ejemplo bruja implementa dos interfaces:

public class MyType : IComparable<MyType>, IComparable 
{ 
    public MyType(string name, int id) 
    { Name = name; Id = id; } 

    public string Name { get; set; } 
    public int Id { get; set; } 

    public int CompareTo(MyType other) 
    { 
     if (null == other) 
      throw new ArgumentNullException("other"); 
     return (Id - other.Id > 0 ? 1 : 0); 
    } 

    public int CompareTo(object other) 
    { 
     if (null == other) 
      throw new ArgumentNullException("other"); 
     if (other is MyType) 
      return (Id - (other as MyType).Id > 0 ? 1 : 0); 
     else 
      throw new InvalidOperationException("Bad type"); 
    } 
} 


MyType t1 = new MyType("a", 1); 
MyType t2 = new MyType("b", 2); 
object someObj = new object(); 

// calls the strongly typed method: CompareTo(MyType other) 
t1.CompareTo(t2); 
// calls the *weakly* typed method: CompareTo(object other) 
t1.CompareTo(someObj); 

Si MyType solo se aplicaba con IComparable<MyType>, el segundo compareTo(someObj) es un tiempo de compilación error. Esta es una de las ventajas de genéricos fuertemente tipados.

Por otro lado, hay métodos en el marco que requieren el IComparable no genérico como Array.Sort. En estos casos, debería considerar implementar ambas interfaces como en este ejemplo.

+1

bruno: en su código, prefiero delegar el trabajo de comparación en CompareTo (objeto) a CompareTo (MyType) en la segunda rama de CompareTo (objeto), en lugar de duplicar el código. – Steve

5

La principal diferencia entre IComparable y IComparable <> es que el primero es pre-genéricos de modo le permite llamar el método de comparar con cualquier objeto, mientras que el segundo hace cumplir que comparte el mismo tipo:

IComparable - CompareTo(object other); 
IComparable<T> - CompareTo(T other); 

Iría con la segunda opción, siempre que no tenga la intención de utilizar ninguna biblioteca .net 1.0 antigua donde los tipos no implementen la solución moderna y genérica. Obtendrás un aumento de rendimiento ya que evitarás el boxeo y las comparaciones no tendrán que comprobar la coincidencia de tipos y también obtendrás la sensación cálida que produce hacer las cosas de la manera más innovadora ...


Para abordar el punto muy bueno y pertinente de Jeff, yo diría que es una buena práctica colocar tan pocas restricciones en un genérico como se requiere para realizar la tarea. Como tiene el control completo del código dentro del genérico, sabe si está utilizando algún método que requiera un tipo básico de IComparable. Por lo tanto, tomando su comentario en consideración Yo personalmente seguir estas reglas:

  • Si no estás esperando el genérico de utilizar cualquier tipo que única implementan IComparable (es decir, el legado 1.0 código) y usted no está llamando a cualquiera los métodos del interior del genérico que se basan en un parámetro IComparable luego usan la restricción IComparable <> solamente.

  • Si está usando tipos que sólo implementan IComparable luego usar esa restricción única

  • Si está utilizando métodos que requieren un parámetro IComparable, pero no el uso de tipos que sólo implementan IComparable a continuación, utilizando tanto las limitaciones como en la respuesta de Jeff aumentará el rendimiento cuando utilice métodos que acepten el tipo genérico.

Para ampliar la tercera regla - Vamos a suponer que la clase que está escribiendo es como sigue:

public class StrangeExample<T> where ... //to be decided 
{ 
    public void SortArray(T[] input) 
    { 
     Array.Sort(input); 
    } 

    public bool AreEqual(T a, T b) 
    { 
     return a.CompareTo(b) == 0; 
    } 
} 

y tenemos que decidir qué restricciones para colocar en él. El método SortArray llama Array.Sort, que requiere que la matriz que se transfiere contenga objetos que implementan IComparable. Por lo tanto, imprescindible tenemos una restricción IComparable:

public class StrangeExample<T> where T : IComparable 

Ahora la clase va a compilar y funcionar correctamente como una matriz de T es válida para Array.Sort y no es un método válido .CompareTo definido en la interfaz. Sin embargo, si está seguro de que usted no tendrá que usar su clase con un tipo que no tenga también implementar el IComparable <> interfaz puede extender su limitación a:

public class StrangeExample<T> where T : IComparable, IComparable<T> 

Esto significa que cuando AreEqual se llamó utilizará el método CompareTo más rápido y genérico y verá un beneficio en el rendimiento a expensas de no poder usarlo con los viejos tipos de .NET 1.0.

Por otro lado, si no tiene el método AreEqual, entonces no hay ninguna ventaja en la restricción IComparable <>, por lo que también puede soltarlo; de todos modos, solo está utilizando implementaciones IComparable.

+1

Esto no es del todo correcto, ya que algunos elementos del BCL solo funcionan con IComparable y no con IComparable , como Array.Sort y ArrayList.Sort. Realmente, la restricción debería hacer cumplir ambas interfaces. –

+0

Gracias Martin. ¿Puedes explicar la última regla que escribiste? ¿Quiere decir que el rendimiento será mejor si tengo ambas interfaces y uso la comparación genérica? –

+0

Sí, dado que la principal ventaja que obtiene al utilizar la restricción <> de IComparable es un beneficio de rendimiento si * debe * admitir el tipo IComparable porque está utilizando métodos que lo requieren (como Array.Sort) pero * puede * admitir el tipo IComparable <> ya que no está utilizando ningún tipo de legado que no lo implemente, entonces es mejor que solicite ambos y use el objeto como el tipo más rápido que pueda. –

1

Usaría la segunda restricción, ya que eso le permitirá hacer referencia a los miembros fuertemente tipados de la interfaz. Si elige su primera opción, tendrá que utilizar el tipo de interfaz.

2

Esas son dos interfaces diferentes. Antes de .NET 2.0 no había genéricos, por lo que solo había IComparable. Con .NET 2.0 llegaron los genéricos y se hizo posible hacer IComparable<T>. Ellos hacen exactamente lo mismo. Básicamente IComparable es obsoleto, aunque la mayoría de las bibliotecas reconocen ambos.

Para hacer que su código sea realmente compatible, implemente ambos, pero haga una llamada a la otra, para que no tenga que escribir el mismo código dos veces.

5

es posible que desee ambas restricciones, como en:

where T : IComparable, IComparable<T> 

Esto haría que el tipo compatible con más usuarios de los IComparable interfaces. La versión genérica de IComparable, IComparable<T> ayudará a evitar el boxeo cuando T sea un tipo de valor y permita implementaciones de los métodos de interfaz con mayor fuerza de tipado. El soporte de ambos garantiza que, independientemente de la interfaz que solicite algún otro objeto, su objeto puede cumplir y, por lo tanto, funcionar bien.

Por ejemplo, Array.Sort y ArrayList.Sort usan IComparable, no IComparable<T>.

Cuestiones relacionadas