Ordenar un List o IList

16/01/2007 - 18:31 por ANT1 | Informe spam
Como se hace para ordenar una lista List<> o IList<> generica de una
clase propia con sus propiedades internas (sus strings, ints, doubles,
...).

He estado mirando las interfaces Icomparable e Icomparer pero soy
incapaz de comprenderlas.

¿Alguien puede echarme un cable?

Preguntas similare

Leer las respuestas

#6 ANT1
17/01/2007 - 16:20 | Informe spam
He estado mirando lo que he podido de todo lo que me comentaste. Ademas
he seguido algunos hilos que he encontrado por ahi sobre IComparable e
IComparer, los cuales parece que hay que implementar propiedad a
propiedad el metodo de comparacion.

De momento tengo hecho esto, siguiendo los pasos que me comentaste.

public class ObjetoComparador<T> where T:IComparable
{
#region Constructor

public ObjetoComparador(string propiedad)
{
Propiedad = propiedad;
}

#endregion

private string propiedad;

public string Propiedad
{
get { return propiedad; }
set { propiedad = value; }
}

public int CompareTo(T obj)
{
Type tipo = typeof(T); // En desarrollo
tipo.GetProperty(Propiedad);
}
}

Pero me surgen unas dudas.

Primero si el constructor esta bien. Creo que si pero bueno eso es
probar.

Segundo, cuando dices usar:

Empleado.Sort(new MiClaseComparadora<Tipo>("Nombre"));

Yo la verdad me referia a "Empleado" como una List<Empleado>, ahi
cometi un error al escribir el hilo y quizas no se me entendio. Pero
mirando el metodo Sort de los List<> he visto que estos solo aceptan
Comparison, IComparer y este ultimo con limitadores. ¿Al pasarle al
Sort el objeto es que reconoce el metodo CompareTo de dicho objeto
automaticamente?

Como veras en el codigo mi metodo CompareTo no hace nada. Y es que no
se como coger la propiedad que se pasa por string del objeto para
ordenarlo por esta. He visto que puedo obtener informacion de las
propiedades por el nombre de estas, pero nada mas. ¿Como se haria
esto?



On 17 ene, 10:59, "ANT1" wrote:
Creeme Alberto cuando digo que me impresionas. Me parece increible todo
lo que conoces de C#. No se cuanto llevaras trabajando en esto pero es
impresionante. Yo con mis 4 meses de experiencia lo flipo.

Respecto a todo lo que me cuentas algunas cosillas como la reflexion
escapan a mi entendimiento o si el parametro que hay que pasar a dicha
clase para decir por que propiedad queremos compararle es Type o
String. Ademas mi trabajo con genericos <T> ha sido muy limitado hasta
el momento. Por todo ello me llevara un tiempo mirarme todas esas
lagunas "basicas" que tengo.

No doy por cerrado este hilo porque ten en cuenta que preguntare mas
cosas y dudas que me surjan. Pero tiempo al tiempo.

Muchas gracias.

On 17 ene, 10:07, "Alberto Poblacion"

wrote:
> "ANT1" wrote in messagenews:

> > [...]
> > Empleado.Sort("Nombre"); La forma de hacer esto es esta:

> Empleado.Sort(new MiClaseComparadora<Tipo>("Nombre"));

> En el constructor de MiClaseComparadora recibes el argumento "Nombre",
> lo guardas en una variable privada de la clase, y ya sabes cuál es el campo
> que hay que comparar. En <Tipo> le pasas la clase cuyos objetos hay que
> comparar, con lo que consigues que el acceso a los objetos sea fuertemente
> tipado. Dentro de la función de comparación en la clase comparadora tendrás
> que usar reflexión para sacar el valor de las propiedades a partir de su
> nombre.

> > vi el otro dia que alguien ponia como
> > generar un objeto de una clase que ya se tenia definida a partr de un
> > string que se pasaba dinamicamente. Algo asi:
> > string clase = "Una Clase Mia";
> > clase Nuevo Objeto = new clase(); Esto se hace con Activator.CreateInstance, pero no es necesario para
> hacer las comparaciones, ya que te llegan creados los objetos a comparar,
> por lo que no necesitas crear una instancia a partir del nombre.

> > Si se pudiese enviar un parametro string con el tipo de clase al
> > Comparision, entonces en este solo habria que hacer algo asi:

> > public static int Compara(object X, object Y, string clase) // Si
> > se pudiese hacer q no lo se
> > {
> > clase ObjetoX = (clase)X;
> > clase ObjetoY = (clase)Y;
> > return ObjetoX.CompareTo(ObjetoY);
> > //Aunque aqui veo que no sabria que clase es ni si tiene el metodo
> > CompareTo(); Esto no valdria de primeras
> > }Mejor algo parecido a lo siguiente:

> public class MiClaseComparadora<Tipo> where Tipo:IComparable :
> IComparer<Tipo>
> {
> public static int Compare<Tipo>(Tipo X, Tipo Y)
> {
> return X.CompareTo(Y); //Aqui, en lugar de comparar X a Y, meterías
> la lógica necesaria para extraer de X y de Y las propiedades que te
> interesen y compararlas.
> }

> } Lo anterior está escrito mu rápido de memoria y me he podido equivocar
> entre IComparer/IComparable o Compare/CompareTo, pero te da una idea de por
> dónde van los tiros.

> > La verdad que lo que he puesto ahi es lo que se me ha ocurrido ahora
> > mismo y tiene una pinta de fallar por todos lados, ya que no se si
> > realmente se podria pasar el parametro "clase", La clase se pasa con Generics, en el tipo del genérico. Si hubiera que
> hacerlo en Framework 1, la pasarías com string y usarías Reflexión (y sería
> mucho más complicado y no te detectaría los errores en tiempo de
> compilación, sino solo en tiempo de ejecución).

> > que al ser una clase desconocida (puede ser cualquiera) no sabria si
> > tene el CompareTo() y no nos dejaria utilizarlo. En Framework 1 sabrías si tiene el CompareTo mediante "if objeto is
> IComparable...". En Framewrok 2 usando Generics no es necesario, porque para
> eso están las restricciones del Generic (lo que se pone al principio de la
> clase detrás del "where..."), que permiten que en tiempo de compilación se
> detecte si se está pasando una clase inadecuada.
Respuesta Responder a este mensaje
#7 Alberto Poblacion
17/01/2007 - 16:54 | Informe spam
"ANT1" wrote in message
news:

Segundo, cuando dices usar:
Empleado.Sort(new MiClaseComparadora<Tipo>("Nombre"));
Yo la verdad me referia a "Empleado" como una List<Empleado>, ahi



Vale, yo había entendido que querías un comparador que comparase
cualquier propiedad de cualquier clase, y por eso había puesto el <Tipo>
para pasar ahi la clase, por ejemplo <Empleado>. Si sabes que la comparación
siempre va a ser de objetos de tipo Empleado, entonces no hace falta usar
Generics para el comparador, y puedes usar simplemente:
empleados.Sort(new MiClaseComparadora("Nombre"));

Te pongo cómo quedaría la clase, incluyendo la función comparadora que
saca las propiedades por reflexión a partir del nombre. Necesitarás un
"using System.Reflecton;". Está todo escrito de memoria, sin probar, así que
no me hago responsable de los fallos :-)

public class MiClaseComparadora : IComparer<Empleado>
{
private string propiedad;

public MiClaseComparadora(string propiedad)
{
this.propiedad = propiedad;
}

public int Compare(Empleado X, Empleado Y)
{
Type t = typeof(Empleado);
PropertyInfo pi = t.GetProperty(propiedad);
object valor1 = pi.GetValue(X, null);
object valor2 = pi.GetValue(Y, null);
return ((IComparable)valor1).CompareTo(valor2);
}
}


Primero si el constructor esta bien. Creo que si pero bueno eso es
probar.



Creo que sí. He copiado tu constructor en el ejemplo de arriba. Por
simplificar he quitado la propiedad pública ya que no la vamos a usar.

En el ejemplo he hecho un "cast" del valor de la propiedad al tipo
IComparable para llamar a su "CompareTo". Lógicamente se producirá un error
si en el nombre de la propiedad se pide una propiedad que no sea de un tipo
IComparable. Con los tipos habituales (números, strings, fechas, etc.), no
hay problema porque todos soportan el IComparable.
Respuesta Responder a este mensaje
#8 ANT1
18/01/2007 - 13:48 | Informe spam
He conseguido implementar mi propio metodo IComparer con genericos. Te
debo mil Alberto.

Al final no he conseguido exactamente lo que queria, que era un metodo
comun para que al hacer un Sort a la lista List<T>, siendo T cualquier
tipo de objeto definido por mi, esta se ordenase por la propiedad que
yo le indicase. Pero he conseguido algo muy parecido.

De primeras te comentare el codigo que me pasaste.

La clase que defines como:

public class MiClaseComparadora : IComparer<Empleado>



No me resultaba util pq le indicas que le indicas que use la clase
"Empleado" concretamente, por lo que en este caso seria mejor
implementar la interface IComparer<Empleado> en la propia clase
Empleado. Y es que no te permite usar genericos ahi. Es decir, no puedo
usar:

public class MiClaseComparadora : IComparer<T>



y despues usar T como la clase que se pasase.

Tambien probe a hacer:

public class MiClaseComparadora : IComparer<Object>



teniendo a mis clases (seguire con el ejemplo de Empleado) heredando de
MiClaseComparadora. Con esto a la hora de hacer el "build solution" me
daba el error que no se podia convertir el IComparer de
MiClaseComparadora a un IComparer de la clase Empleado (ya que el
List<> a comparar era List<Empleado>), es decir no me aceptaba la
equivalencia.

La solucion que he tomado ha sido implementar un Comparer para cada
clase como tu indicaste, con su string con la propiedad para ordenar,
el constructor adecuado q coja dicho string y el metodo Compare
identico al que me pasaste. Quedando esto (omitiendo todas las
propiedades, metodos y demas):

public class Empleado:IComparer<Empleado>
{

public Empleado(){} // Constructor vacio para que
funcionase todo lo que ya tenia

public Empleado(string propiedad) // Constructor especial para
realizar el Sort
{
this.propSort = propiedad;
}

private string propSort;

public int Compare(Empleado x, Empleado y)
{
Type tipo = typeof(Empleado);
PropertyInfo pi = tipo.GetProperty(this.propSort);
Object valor1 = pi.GetValue(x, null);
Object valor2 = pi.GetValue(y, null);
return ((IComparable)valor1).CompareTo(valor2);
}

}

En el codigo de la aplicacion hago:

List<Empleado> lista = .;
lista.Sort(new Empleado("PropiedadDeOrden");

Y a correr.

Es una lata tener que implementar ese codigo para cada clase que tengo.
Pero la verdad es un codigo minimo en el que hay que cambiar el nombre
de la clase (es decir, practiacmente es cortar y pegar) y es mucho
mejor que tener que realizar Comparison de cada propiedad de mi clase
(a ojo de buen cubero, unas ciento y pico lineas menos de codigo por
clase, en total unas 4000 lienas de codigo de aplicacion que me
ahorro).

Si te comento todo esto no es por tratar de mostrar que estabas
equivocado, no confundamos, ni te imaginas lo agradecido que estoy
porque con esto llevaba un monton de tiempo y ya has visto la cantidad
de codigo que me ahorro. Hay otro motivos. Primero que asi cualquiera
que le pueda ser util este hilo lo use con codigo ya probado y
funcional, y segundo, y principal, por si ves que cometi algun error o
crees que he pasado algo por algo al hacer esto me lo digas, yo estoy
aun aprendiendo, y la verdad no hay nada peor que aprender mal y
arrastrarlo toda tu vida.

Muchas gracias por todo.



On 17 ene, 16:54, "Alberto Poblacion"
wrote:
"ANT1" wrote in messagenews:

> Segundo, cuando dices usar:
> Empleado.Sort(new MiClaseComparadora<Tipo>("Nombre"));
> Yo la verdad me referia a "Empleado" como una List<Empleado>, ahi Vale, yo había entendido que querías un comparador que comparase
cualquier propiedad de cualquier clase, y por eso había puesto el <Tipo>
para pasar ahi la clase, por ejemplo <Empleado>. Si sabes que la comparación
siempre va a ser de objetos de tipo Empleado, entonces no hace falta usar
Generics para el comparador, y puedes usar simplemente:
empleados.Sort(new MiClaseComparadora("Nombre"));

Te pongo cómo quedaría la clase, incluyendo la función comparadora que
saca las propiedades por reflexión a partir del nombre. Necesitarás un
"using System.Reflecton;". Está todo escrito de memoria, sin probar, así que
no me hago responsable de los fallos :-)

public class MiClaseComparadora : IComparer<Empleado>
{
private string propiedad;

public MiClaseComparadora(string propiedad)
{
this.propiedad = propiedad;
}

public int Compare(Empleado X, Empleado Y)
{
Type t = typeof(Empleado);
PropertyInfo pi = t.GetProperty(propiedad);
object valor1 = pi.GetValue(X, null);
object valor2 = pi.GetValue(Y, null);
return ((IComparable)valor1).CompareTo(valor2);
}

}
> Primero si el constructor esta bien. Creo que si pero bueno eso es
> probar. Creo que sí. He copiado tu constructor en el ejemplo de arriba. Por
simplificar he quitado la propiedad pública ya que no la vamos a usar.

En el ejemplo he hecho un "cast" del valor de la propiedad al tipo
IComparable para llamar a su "CompareTo". Lógicamente se producirá un error
si en el nombre de la propiedad se pide una propiedad que no sea de un tipo
IComparable. Con los tipos habituales (números, strings, fechas, etc.), no
hay problema porque todos soportan el IComparable.
email Siga el debate Respuesta Responder a este mensaje
Ads by Google
Help Hacer una pregunta AnteriorRespuesta Tengo una respuesta
Search Busqueda sugerida