2008-08-19 17 views
68

Me encontré hoy con un problema interesante. Tenemos un servicio web de WCF que devuelve un IList. No fue realmente un gran problema hasta que quise ordenarlo.Ordenando un IList en C#

Resulta que la interfaz IList no tiene un método para ordenar construido en.

Terminé usando el método ArrayList.Adapter(list).Sort(new MyComparer()) para resolver el problema, pero parecía un "gueto" poco a mí.

Jugué con la escritura de un método de extensión, también heredando de IList e implementando mi propio método Sort(), así como el lanzamiento a una lista, pero ninguno de estos parecía demasiado elegante.

Así que mi pregunta es, ¿alguien tiene una solución elegante para ordenar un IList

+0

¿Por qué devolverías un IList en primer lugar? De un servicio WCF? – DaeMoohn

Respuesta

51

¿Cómo sobre el uso de LINQ to Objects para ordenar para usted?

Digamos que tienes una IList<Car>, y el coche tenía una propiedad Engine, creo que se podría clasificar de la siguiente manera:

from c in list 
orderby c.Engine 
select c; 

Editar: Usted tiene que ser rápido para conseguir respuestas aquí. Como presenté una sintaxis ligeramente diferente a las otras respuestas, dejaré mi respuesta; sin embargo, las otras respuestas presentadas son igualmente válidas.

+0

Creará un nuevo enumerable, que puede no ser deseable en algunos escenarios. No puede ordenar un IList en el lugar a través de la interfaz, excepto mediante el uso del método ArrayList.Adapter en mi conocimiento. –

9

Vas a tener que hacer algo así, creo que (convertirlo en un tipo más concreto).

Quizás llévela a una Lista de T en lugar de a ArrayList, de modo que obtenga seguridad de tipo y más opciones sobre cómo implementar el comparador.

2

convertir su IList en List<T> o alguna otra colección genérica y luego se puede consultar fácilmente/especie usando System.Linq espacio de nombres (que suministrará montón de métodos de extensión)

+6

'IList ' implementa 'IEnumerable ' y, por lo tanto, no es necesario convertirlo para utilizar las operaciones de Linq. –

56

Puede utilizar LINQ:

using System.Linq; 

IList<Foo> list = new List<Foo>(); 
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar); 
IList<Foo> sortedList = sortedEnum.ToList(); 
0

Aquí hay un ejemplo usando la tipificación más fuerte. Sin embargo, no estoy seguro si es necesariamente la mejor manera.

static void Main(string[] args) 
{ 
    IList list = new List<int>() { 1, 3, 2, 5, 4, 6, 9, 8, 7 }; 
    List<int> stronglyTypedList = new List<int>(Cast<int>(list)); 
    stronglyTypedList.Sort(); 
} 

private static IEnumerable<T> Cast<T>(IEnumerable list) 
{ 
    foreach (T item in list) 
    { 
     yield return item; 
    } 
} 

La función de conversión es sólo una reimplementación del método de extensión que viene con 3,5 escrita como un método estático normal. Es bastante desagradable y verboso por desgracia.

0

En VS2008, cuando hago clic en la referencia del servicio y selecciono "Configurar referencia de servicio", hay una opción para elegir cómo el cliente deserializa las listas devueltas desde el servicio.

En particular, puedo elegir entre System.array, System.Collections.ArrayList y System.Collections.Generic.List

0

encontrado un buen puesto en esto y pensé en compartir. Check it out HERE

Básicamente.

puede crear la siguiente clase y clases IComparer

public class Widget { 
    public string Name = string.Empty; 
    public int Size = 0; 

    public Widget(string name, int size) { 
    this.Name = name; 
    this.Size = size; 
} 
} 

public class WidgetNameSorter : IComparer<Widget> { 
    public int Compare(Widget x, Widget y) { 
     return x.Name.CompareTo(y.Name); 
} 
} 

public class WidgetSizeSorter : IComparer<Widget> { 
    public int Compare(Widget x, Widget y) { 
    return x.Size.CompareTo(y.Size); 
} 
} 

Entonces Si usted tiene un IList, puede ordenar así.

List<Widget> widgets = new List<Widget>(); 
widgets.Add(new Widget("Zeta", 6)); 
widgets.Add(new Widget("Beta", 3)); 
widgets.Add(new Widget("Alpha", 9)); 

widgets.Sort(new WidgetNameSorter()); 
widgets.Sort(new WidgetSizeSorter()); 

Pero la comprobación de este sitio para más información ... Check it out HERE

0
using System.Linq; 

var yourList = SomeDAO.GetRandomThings(); 
yourList.ToList().Sort((thing, randomThing) => thing.CompareThisProperty.CompareTo(randomThing.CompareThisProperty)); 

Eso es! Bastante gueto.

1

Encontré este hilo cuando estaba buscando una solución al problema exacto descrito en la publicación original. Sin embargo, ninguna de las respuestas satisfizo mi situación por completo. La respuesta de Brody fue bastante cercana. Aquí está mi situación y solución que encontré.

Tengo dos ILists del mismo tipo devueltos por NHibernate y han surgido los dos IList en uno, de ahí la necesidad de ordenar.

Como Brody dijo He implementado un COMPARAR sobre el objeto (ReportFormat) que es el tipo de mi IList:

public class FormatCcdeSorter:IComparer<ReportFormat> 
    { 
     public int Compare(ReportFormat x, ReportFormat y) 
     { 
      return x.FormatCode.CompareTo(y.FormatCode); 
     } 
    } 

entonces convertir el fusionada IList a una matriz del mismo tipo:

ReportFormat[] myReports = new ReportFormat[reports.Count]; //reports is the merged IList 

Entonces ordenar la matriz:

Array.Sort(myReports, new FormatCodeSorter());//sorting using custom comparer 

Desde matriz unidimensional implementa el i nterface System.Collections.Generic.IList<T>, la matriz se puede utilizar como el IList original.

+0

esta es la forma correcta de hacerlo. – user29964

0

¿Es esta una solución válida?

 IList<string> ilist = new List<string>(); 
     ilist.Add("B"); 
     ilist.Add("A"); 
     ilist.Add("C"); 

     Console.WriteLine("IList"); 
     foreach (string val in ilist) 
      Console.WriteLine(val); 
     Console.WriteLine(); 

     List<string> list = (List<string>)ilist; 
     list.Sort(); 
     Console.WriteLine("List"); 
     foreach (string val in list) 
      Console.WriteLine(val); 
     Console.WriteLine(); 

     list = null; 

     Console.WriteLine("IList again"); 
     foreach (string val in ilist) 
      Console.WriteLine(val); 
     Console.WriteLine(); 

El resultado fue: IList B A C

Lista A B C

IList de nuevo A B C

+0

Válido si es realmente una Lista . En algunos casos, tiene otros tipos implementando IList (por ejemplo, una matriz simple) donde el downcast no funcionaría. Lástima que el método Sort() no sea un método de extensión para IList . – Cygon

1

Útil para rejilla separadora este método clasifica la lista basado en nombres de propiedad. Como sigue el ejemplo.

List<MeuTeste> temp = new List<MeuTeste>(); 

    temp.Add(new MeuTeste(2, "ramster", DateTime.Now)); 
    temp.Add(new MeuTeste(1, "ball", DateTime.Now)); 
    temp.Add(new MeuTeste(8, "gimm", DateTime.Now)); 
    temp.Add(new MeuTeste(3, "dies", DateTime.Now)); 
    temp.Add(new MeuTeste(9, "random", DateTime.Now)); 
    temp.Add(new MeuTeste(5, "call", DateTime.Now)); 
    temp.Add(new MeuTeste(6, "simple", DateTime.Now)); 
    temp.Add(new MeuTeste(7, "silver", DateTime.Now)); 
    temp.Add(new MeuTeste(4, "inn", DateTime.Now)); 

    SortList(ref temp, SortDirection.Ascending, "MyProperty"); 

    private void SortList<T>(
    ref List<T> lista 
    , SortDirection sort 
    , string propertyToOrder) 
    { 
     if (!string.IsNullOrEmpty(propertyToOrder) 
     && lista != null 
     && lista.Count > 0) 
     { 
      Type t = lista[0].GetType(); 

      if (sort == SortDirection.Ascending) 
      { 
       lista = lista.OrderBy(
        a => t.InvokeMember(
         propertyToOrder 
         , System.Reflection.BindingFlags.GetProperty 
         , null 
         , a 
         , null 
        ) 
       ).ToList(); 
      } 
      else 
      { 
       lista = lista.OrderByDescending(
        a => t.InvokeMember(
         propertyToOrder 
         , System.Reflection.BindingFlags.GetProperty 
         , null 
         , a 
         , null 
        ) 
       ).ToList(); 
      } 
     } 
    } 
49

Esta pregunta me inspiró a escribir una entrada de blog: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

Creo que, idealmente, .NET Framework incluiría un método de clasificación estática que acepta un IList <T>, pero la siguiente mejor Lo importante es crear su propio método de extensión. No es demasiado difícil crear un par de métodos que le permitan ordenar un IList <T> como lo haría con una Lista <T>. Como beneficio adicional, puede sobrecargar el método de extensión LINQ OrderBy usando la misma técnica, de modo que ya sea que use List.Sort, IList.Sort o IEnumerable.OrderBy, puede usar la misma sintaxis.

public static class SortExtensions 
{ 
    // Sorts an IList<T> in place. 
    public static void Sort<T>(this IList<T> list, Comparison<T> comparison) 
    { 
     ArrayList.Adapter((IList)list).Sort(new ComparisonComparer<T>(comparison)); 
    } 

    // Convenience method on IEnumerable<T> to allow passing of a 
    // Comparison<T> delegate to the OrderBy method. 
    public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison) 
    { 
     return list.OrderBy(t => t, new ComparisonComparer<T>(comparison)); 
    } 
} 

// Wraps a generic Comparison<T> delegate in an IComparer to make it easy 
// to use a lambda expression for methods that take an IComparer or IComparer<T> 
public class ComparisonComparer<T> : IComparer<T>, IComparer 
{ 
    private readonly Comparison<T> _comparison; 

    public ComparisonComparer(Comparison<T> comparison) 
    { 
     _comparison = comparison; 
    } 

    public int Compare(T x, T y) 
    { 
     return _comparison(x, y); 
    } 

    public int Compare(object o1, object o2) 
    { 
     return _comparison((T)o1, (T)o2); 
    } 
} 

Con estas extensiones, ordenar su IList igual que lo haría una lista:

IList<string> iList = new [] 
{ 
    "Carlton", "Alison", "Bob", "Eric", "David" 
}; 

// Use the custom extensions: 

// Sort in-place, by string length 
iList.Sort((s1, s2) => s1.Length.CompareTo(s2.Length)); 

// Or use OrderBy() 
IEnumerable<string> ordered = iList.OrderBy((s1, s2) => s1.Length.CompareTo(s2.Length)); 

Hay más información en el mensaje: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

+0

El enfoque correcto realmente habría sido ofrecer una interfaz 'ISortableList ' (con métodos para ordenar una parte de la lista usando un comparador particular), tener 'List ' implementarlo, y tener un método estático que pudiera ordenar cualquier ' IList 'comprobando si implementó' ISortableList 'y, si no, copiándolo en una matriz, ordenando eso, borrando el' IList ', y volviendo a agregar los elementos. – supercat

+3

¡Maravillosa respuesta! Sin embargo, una advertencia: este enfoque asume que la lista 'IList ' se puede convertir a la interfaz' IList' no genérica. Si codifica su propia clase implementando la interfaz 'IList ', asegúrese de implementar también la interfaz 'IList' no genérica, o el código fallará con una excepción de lanzamiento de clase. – sstan

0
try this **USE ORDER BY** : 

    public class Employee 
    { 
     public string Id { get; set; } 
     public string Name { get; set; } 
    } 

private static IList<Employee> GetItems() 
     { 
      List<Employee> lst = new List<Employee>(); 

      lst.Add(new Employee { Id = "1", Name = "Emp1" }); 
      lst.Add(new Employee { Id = "2", Name = "Emp2" }); 
      lst.Add(new Employee { Id = "7", Name = "Emp7" }); 
      lst.Add(new Employee { Id = "4", Name = "Emp4" }); 
      lst.Add(new Employee { Id = "5", Name = "Emp5" }); 
      lst.Add(new Employee { Id = "6", Name = "Emp6" }); 
      lst.Add(new Employee { Id = "3", Name = "Emp3" }); 

      return lst; 
     } 

**var lst = GetItems().AsEnumerable(); 

      var orderedLst = lst.OrderBy(t => t.Id).ToList(); 

      orderedLst.ForEach(emp => Console.WriteLine("Id - {0} Name -{1}", emp.Id, emp.Name));** 
4

La respuesta aceptada por @DavidMills es bastante bueno, pero creo que se puede mejorar. Por un lado, no hay necesidad de definir la clase ComparisonComparer<T> cuando el marco ya incluye un método estático Comparer<T>.Create(Comparison<T>). Este método se puede usar para crear un IComparison sobre la marcha.

También, arroja IList<T> a IList que tiene el potencial de ser peligroso. En la mayoría de los casos que he visto, List<T> que implementa IList se utiliza detrás de las escenas para implementar IList<T>, pero esto no está garantizado y puede conducir a código frágil.

Por último, el método sobrecargado List<T>.Sort() tiene 4 firmas y solo se implementan 2 de ellas.

  1. List<T>.Sort()
  2. List<T>.Sort(Comparison<T>)
  3. List<T>.Sort(IComparer<T>)
  4. List<T>.Sort(Int32, Int32, IComparer<T>)

La clase debajo implementa todos 4 List<T>.Sort() firmas para el interfaz IList<T>:

public static class IListExtensions 
{ 
    public static void Sort<T>(this IList<T> list) 
    { 
     if (list is List<T>) 
     { 
      ((List<T>)list).Sort(); 
     } 
     else 
     { 
      List<T> copy = new List<T>(list); 
      copy.Sort(); 
      Copy(copy, 0, list, 0, list.Count); 
     } 
    } 

    public static void Sort<T>(this IList<T> list, Comparison<T> comparison) 
    { 
     if (list is List<T>) 
     { 
      ((List<T>)list).Sort(comparison); 
     } 
     else 
     { 
      List<T> copy = new List<T>(list); 
      copy.Sort(comparison); 
      Copy(copy, 0, list, 0, list.Count); 
     } 
    } 

    public static void Sort<T>(this IList<T> list, IComparer<T> comparer) 
    { 
     if (list is List<T>) 
     { 
      ((List<T>)list).Sort(comparer); 
     } 
     else 
     { 
      List<T> copy = new List<T>(list); 
      copy.Sort(comparer); 
      Copy(copy, 0, list, 0, list.Count); 
     } 
    } 

    public static void Sort<T>(this IList<T> list, int index, int count, 
     IComparer<T> comparer) 
    { 
     if (list is List<T>) 
     { 
      ((List<T>)list).Sort(index, count, comparer); 
     } 
     else 
     { 
      List<T> range = new List<T>(count); 
      for (int i = 0; i < count; i++) 
      { 
       range.Add(list[index + i]); 
      } 
      range.Sort(comparer); 
      Copy(range, 0, list, index, count); 
     } 
    } 

    private static void Copy(IList<T> sourceList, int sourceIndex, 
     IList<T> destinationList, int destinationIndex, int count) 
    { 
     for (int i = 0; i < count; i++) 
     { 
      destinationList[destinationIndex + i] = sourceList[sourceIndex + i]; 
     } 
    } 
} 

Uso:

class Foo 
{ 
    public int Bar; 

    public Foo(int bar) { this.Bar = bar; } 
} 

void TestSort() 
{ 
    IList<int> ints = new List<int>() { 1, 4, 5, 3, 2 }; 
    IList<Foo> foos = new List<Foo>() 
    { 
     new Foo(1), 
     new Foo(4), 
     new Foo(5), 
     new Foo(3), 
     new Foo(2), 
    }; 

    ints.Sort(); 
    foos.Sort((x, y) => Comparer<int>.Default.Compare(x.Bar, y.Bar)); 
} 

La idea es aprovechar la funcionalidad de la List<T> subyacente para manejar la clasificación siempre que sea posible. De nuevo, la mayoría de las implementaciones IList<T> que he visto usan esto. En el caso en que la colección subyacente sea de un tipo diferente, retroceda a la creación de una nueva instancia de List<T> con elementos de la lista de entrada, utilícela para realizar la clasificación y luego copie los resultados nuevamente en la lista de entrada. Esto funcionará incluso si la lista de entrada no implementa la interfaz IList.