sort por ordenar objetos nombre lista interfaz interfaces implementacion comparer c# .net generics interface icomparable

objetos - ordenar lista por nombre c#



Restricción de interfaz para IComparable (5)

Cuando quiero restringir el tipo T para que sea comparable, ¿debería usar:

where T : IComparable

o

where T : IComparable<T>

No puedo entender si el # 2 tiene sentido. ¿Alguien puede explicar cuál sería la diferencia?


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

Tu puedes tener

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

como oponerse a

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

Tomemos como ejemplo el siguiente ejemplo que bruja implementa ambas 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 implementó con IComparable<MyType> , el segundo compareTo(someObj) es un error de tiempo de compilación. Esta es una ventaja de los 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.


Es posible que desee ambas restricciones, como en:

where T : IComparable, IComparable<T>

Esto haría que tu tipo sea compatible con más usuarios de las interfaces IComparable . La versión genérica de IComparable , IComparable<T> ayudará a evitar el boxeo cuando T es un tipo de valor y permite implementaciones más fuertemente tipadas de los métodos de interfaz. 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> .


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 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.


La principal diferencia entre IComparable e IComparable <> es que el primero es pre-genérico, por lo que le permite llamar al método de comparación con cualquier objeto, mientras que el segundo exige que comparta el mismo tipo:

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

Me gustaría ir 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, argumentaría que es una buena práctica colocar tan pocas restricciones en un genérico como se requiera 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. Entonces, teniendo en cuenta su comentario, yo personalmente seguiría estas reglas:

  • Si no espera que el genérico use ningún tipo que solo implemente IComparable (es decir, el código heredado 1.0) y no está llamando a ningún método desde el genérico que se base en un parámetro IComparable, entonces use la restricción IComparable <> solamente.

  • Si está utilizando tipos que solo implementan IComparable, use esa restricción solo

  • Si está utilizando métodos que requieren un parámetro IComparable, pero no utiliza tipos que solo implementan IComparable, entonces usar ambas restricciones como en la respuesta de Jeff aumentará el rendimiento cuando use métodos que acepten el tipo genérico.

Para expandir la tercera regla, supongamos que la clase que estás escribiendo es la siguiente:

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 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, debemos tener una restricción IComparable:

public class StrangeExample<T> where T : IComparable

Ahora la clase compilará y funcionará correctamente ya que una matriz de T es válida para Array.Sort y hay un método válido .CompareTo definido en la interfaz. Sin embargo, si está seguro de que no querrá utilizar su clase con un tipo que tampoco implemente la interfaz IComparable <>, puede ampliar su restricción a:

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

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

Por otro lado, si no tenía el método AreEqual, entonces no hay ninguna ventaja para la restricción IComparable <>, así que también puede soltarlo; de todos modos, solo está usando implementaciones IComparable.


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