2008-10-22 7 views
5

Tengo un IList<T> que necesito ordenar, y prefiero no copiar la lista si es posible. Me he dado cuenta de que ArrayList tiene un método estático Adapter que envuelve la lista pasada sin copiarla, pero esto requiere un IList y tengo un IList<T>. ¿Es seguro lanzar de System.Collections.Generic.IList<T> a System.Collections.IList y solo usar el método Adapter?¿Cuál es la mejor manera de ordenar un IList <T> en .Net 2.0?

Tenga en cuenta que esto es .Net 2.0, por lo que LINQ no es una opción.

Respuesta

13

Desde el blog de Paul Fox, recomiendo el post "Cómo ordenar un IList": http://foxsys.blogspot.com/2007/06/how-to-sort-generic-ilist.html

Sólo en caso de que el blog se va en el futuro, voy a copiar el post aquí:


Cómo ordenar un IList genérico

actualización

puede leer y actualizado después Abou t sorting generic IList and List. Mucha gente preferirá los métodos mencionados en la publicación actualizada.

Ordenar una IList genérico

yo estaba tratando de ordenar un IList genérico <> y encontró una manera bastante sencilla de hacerlo.

Paso 1

Es necesario implementar IComparable para el tipo de contenido en su IList. Para este ejemplo, voy a usar una clase simple de lenguaje Dto.

public class LanguageDto : IComparable { 
private String name; 
public string Name { get { return name; } set { name = value; } } 

public LanguageDto(string name) { 
    this.name = name; 
} 

#region IComparable Members 
public int CompareTo(object obj) { 
    if (obj is LanguageDto) { 
    LanguageDto language = (LanguageDto)obj; 
    return this.name.CompareTo(language.name); 
    } 
    throw new ArgumentException(string.Format("Cannot compare a LanguageDto to an {0}", obj.GetType().ToString())); 
} 
#endregion 
} 

PASO 2

Ordenar su IList. Para hacer esto, usará el método ArrayList.Adapter() que pasa su IList y luego llama al método Sort. Al igual que ...

ArrayList.Adapter((IList)languages).Sort(); 

Nota: idiomas es de tipo "IList"

Idiomas debería ser entonces una lista ordenada de su tipo!

+2

para que no solo tire la IList a un IList. ¿Por qué es un lanzamiento seguro? –

+0

No copie la publicación completa. Parecería que es lo correcto para asegurarse de que se preserve el contenido, pero la copia de toda la publicación probablemente requiera un permiso explícito del titular de los derechos de autor. Sin embargo, puedes y debes crear un resumen. –

+0

@EddieDeyo: Sé que esto tiene algunos años, pero es porque IList realmente no existe, sino que es un IList de LanguageDto, que por supuesto es un IList de LanguageDto. T es (principalmente) una ilusión de tiempo de compilación. La "diferencia" entre un IList e IList es que para IList Item [0] devuelve un Object, pero para IList Item [0] devuelve un Object que siempre será una T, y si T es una struct/primitive the Object no habrá sido encuadrado Entonces, un elenco de IList a IList sería seguro (innecesario, pero seguro) pero el reverso no es – jmoreno

6

No se puede convertir IList (T) a IList.

Después de algún olfateando con reflector, parece que ArrayList.Adapter (IList) .Sort() se copie primero la lista de una serie de objetos, ordenar la matriz y luego copiar la matriz de nuevo a una lista:

object[] array = new object[count]; 
this.CopyTo(index, array, 0, count); 
Array.Sort(array, 0, count, comparer); 
for (int i = 0; i < count; i++) 
{ 
    this._list[i + index] = array[i]; 
} 

Puede obtener sobrecarga de boxeo si T en su Lista (T) es un tipo de valor.

Si necesita alterar la secuencia de los objetos de la lista que usted tiene, lo puede hacer de manera similar:

IList<object> unsorted = ... 
List<object> sorted = new List<object>(unsorted); 
sorted.Sort(); 
for (int i = 0; i < unsorted.Countt; i++) 
{ 
    unsorted[i] = sorted[i]; 
} 

Si la lista es tan grande (como en cientos de millones de artículos) que se no puedo hacer una copia adicional en la memoria, sugiero usar una Lista (T) en primer lugar o implementar su algoritmo de ordenamiento in situ favorito.

+0

Sí, pero dije que esperaba evitar copiar la lista. –

+0

no lo copie entonces y simplemente diga: sin clasificar = ordenado; –

+0

@Hallgrim: seguro que puedes. http://ideone.com/bqzIpk4 'IList x = new int [] {1,2,3, 4}; IList g = (IList ) x; System.Console.WriteLine (g.Count); IList g2 = g como IList; System.Console.WriteLine (g2.Count); ' – jmoreno

0

Sé que no es .NET 2.0 pero me encanta LINQ tanto y se aprueba que cada vez que puedo :)

Ordenar simple:

var sortedProducts = 
    from p in products 
    orderby p.ProductName 
    select p; 

ObjectDumper.Write(sortedProducts); 

Ordenar por múltiples condiciones :

string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; 

var sortedDigits = 
    from d in digits 
    orderby d.Length, d 
    select d; 

Ambos ejemplos son de 101 Linq Samples

+1

Esto es bueno. A través de la magia de google, es probable que alguien llegue aquí en busca de ayuda, que no comparte la restricción de OP 2.0. –

1

Dado que el método Sort no está en la interfaz IList usted podría considerar la creación de su propia:

interface ISortableList<T> : IList<T> 
{ 
    void Sort(); 
    void Sort(IComparer<T> comparer); 
} 

class SortableList<T> : List<T>, ISortableList<T> { } 

/* usage */ 
void Example(ISortedList<T> list) 
{ 
    list.Sort(); 
    list.Sort(new MyCustomerComparer()); 
} 

En general, el tipo de parámetro que se especifica en el método debería ser el mínimo común denominador de los miembros que realmente necesita llamar. Si realmente necesita llamar al método Sort(), entonces su parámetro debe tener definido ese miembro. De lo contrario, probablemente debería cargarlo en otro objeto que pueda hacer lo que quiera, tales como:

void Example(IList<T> list) 
{ 
    list = new List<T>(list).Sort(); 
} 

En realidad, esto debería ser bastante rápido, casi con toda seguridad aún más rápido que escribir su propio algoritmo de ordenación personalizada en línea.

-3
IList<object> unsorted = ... 
IList<object> sortedList = unsorted.Orderby(x => x.Tostring()).Tolist(); 

esto le dará la lista ordenada en el campo particular del objeto.

0

Si necesita ordenar listas (no ILists) de diferentes clases sin la necesidad de crear una clase comparer separada para todas ellas y aún mantener sus clases de entidad limpias (no desea implementar IComparable), puede utilice la siguiente (compatible con .NET 2.0):

public class DynamicComparer<T> : IComparer<T> 
{ 

    private Func<T, int> calculateFunc; 
    private int calculateMultiplier; 

    private Func<T, T, int> compareFunc; 
    public DynamicComparer(Func<T, int> calculateFunc, bool reverse = false) 
    { 
     if (calculateFunc == null) 
     { 
      throw new Exception("Delegate function 'calculateFunc' cannot be null."); 
     } 

     this.calculateFunc = calculateFunc; 
     this.calculateMultiplier = reverse ? -1 : 1; 
     this.compareFunc = null; 
    } 

    public DynamicComparer(Func<T, T, int> compareFunc) 
    { 
     if (calculateFunc == null) 
     { 
      throw new Exception("Delegate function 'compareFunc' cannot be null."); 
     } 

     this.calculateFunc = null; 
     this.compareFunc = compareFunc; 
    } 

    public int Compare(T x, T y) 
    { 
     if (calculateFunc != null) 
     { 
      return (calculateFunc(x) - calculateFunc(y)) * this.calculateMultiplier; 
     } 
     if (compareFunc != null) 
     { 
      return compareFunc(x, y); 
     } 

     throw new Exception("Compare not possible because neither a Compare or a Calculate function was specified."); 
    } 
} 

también debes cumplir los delegados Func si está utilizando .NET 2.0 (que se encuentra en Replacing Func with delegates C#):

public delegate TResult Func<T, TResult>(T t); 
public delegate TResult Func<T, U, TResult>(T t, U u); 

uso:

myList.Sort(new DynamicComparer<MyClass>(x => x.MyIntProperty) // Ascending 
myList.Sort(new DynamicComparer<MyClass>(x => x.MyIntProperty, true) // Descending 

Algunos sencilla prueba de la unidad:

[TestClass()] 
public class DynamicComparerTU 
{ 
    [TestMethod()] 
    public void SortIntList() 
    { 
     // Arrange 
     dynamic myIntArray = new int[] { 
      4, 
      1, 
      9, 
      0, 
      4, 
      7 
     }; 
     dynamic myIntList = new List<int>(myIntArray); 

     // Act 
     int temp = 0; 
     for (int write = 0; write <= myIntArray.Length - 1; write++) 
     { 
      for (int sort = 0; sort <= myIntArray.Length - 2; sort++) 
      { 
       if (myIntArray(sort) > myIntArray(sort + 1)) 
       { 
        temp = myIntArray(sort + 1); 
        myIntArray(sort + 1) = myIntArray(sort); 
        myIntArray(sort) = temp; 
       } 
      } 
     } 

     myIntList.Sort(new DynamicComparer<int>(x => x)); 

     // Assert 
     Assert.IsNotNull(myIntList); 
     Assert.AreEqual(myIntArray.Length, myIntList.Count); 
     for (int i = 0; i <= myIntArray.Length - 1; i++) 
     { 
      Assert.AreEqual(myIntArray(i), myIntList(i)); 
     } 
    } 

    [TestMethod()] 
    public void SortStringListByLength() 
    { 
     // Arrange 
     dynamic myStringArray = new string[] { 
      "abcd", 
      "ab", 
      "abcde", 
      "a", 
      "abc" 
     }; 
     dynamic myStringList = new List<string>(myStringArray); 

     // Act 
     myStringList.Sort(new DynamicComparer<string>(x => x.Length)); 

     // Assert 
     Assert.IsNotNull(myStringList); 
     Assert.AreEqual(5, myStringList.Count); 
     Assert.AreEqual("a", myStringList(0)); 
     Assert.AreEqual("ab", myStringList(1)); 
     Assert.AreEqual("abc", myStringList(2)); 
     Assert.AreEqual("abcd", myStringList(3)); 
     Assert.AreEqual("abcde", myStringList(4)); 
    } 

    [TestMethod()] 
    public void SortStringListByLengthDescending() 
    { 
     // Arrange 
     dynamic myStringArray = new string[] { 
      "abcd", 
      "ab", 
      "abcde", 
      "a", 
      "abc" 
     }; 
     dynamic myStringList = new List<string>(myStringArray); 

     // Act 
     myStringList.Sort(new DynamicComparer<string>(x => x.Length, true)); 

     // Assert 
     Assert.IsNotNull(myStringList); 
     Assert.AreEqual(5, myStringList.Count); 
     Assert.AreEqual("abcde", myStringList(0)); 
     Assert.AreEqual("abcd", myStringList(1)); 
     Assert.AreEqual("abc", myStringList(2)); 
     Assert.AreEqual("ab", myStringList(3)); 
     Assert.AreEqual("a", myStringList(4)); 
    } 
} 
Cuestiones relacionadas