【问题标题】:Sorting an IList in C#在 C# 中对 IList 进行排序
【发布时间】:2023-04-07 00:06:01
【问题描述】:

所以我今天遇到了一个有趣的问题。我们有一个返回 IList 的 WCF Web 服务。在我想对其进行排序之前,这没什么大不了的。

原来IList接口没有内置排序方法。

我最终使用ArrayList.Adapter(list).Sort(new MyComparer()) 方法来解决问题,但它对我来说似乎有点“贫民窟”。

我玩弄过编写扩展方法,也尝试从 IList 继承并实现我自己的 Sort() 方法以及强制转换为 List,但这些似乎都不过分优雅。

所以我的问题是,有没有人有一个优雅的解决方案来排序 IList

【问题讨论】:

  • 你为什么要首先返回一个 IList?来自 WCF 服务?

标签: c# generics sorting ilist


【解决方案1】:

您可以使用 LINQ:

using System.Linq;

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

【讨论】:

    【解决方案2】:

    这个问题启发我写了一篇博文:http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

    我认为,理想情况下,.NET Framework 将包含一个接受 IList 的静态排序方法,但下一个最好的方法是创建自己的扩展方法。创建几个允许您像对 List 一样对 IList 进行排序的方法并不难。作为奖励,您可以使用相同的技术重载 LINQ OrderBy 扩展方法,这样无论您使用 List.Sort、IList.Sort 还是 IEnumerable.OrderBy,都可以使用完全相同的语法。

    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));
        }
    
        // Sorts in IList<T> in place, when T is IComparable<T>
        public static void Sort<T>(this IList<T> list) where T: IComparable<T>
        {
            Comparison<T> comparison = (l, r) => l.CompareTo(r);
            Sort(list, 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);
        }
    }
    

    使用这些扩展,您可以像对列表一样对 IList 进行排序:

    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));
    

    帖子中有更多信息:http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

    【讨论】:

    • 正确的方法是提供一个ISortableList&lt;T&gt; 接口(使用一些特定的比较器对列表的一部分进行排序的方法),让List&lt;T&gt; 实现它,并有一个静态方法它可以通过检查它是否实现ISortableList&lt;T&gt; 来对任何IList&lt;T&gt; 进行排序,如果没有,则将其复制到数组中,对其进行排序,清除IList&lt;T&gt;,然后重新添加项目。
    • 精彩的答案!但是,请注意:此方法假定 IList&lt;T&gt; list 可以强制转换为非通用 IList 接口。如果您编写自己的类来实现IList&lt;T&gt; 接口,请确保您还实现了非泛型IList 接口,否则代码将因类转换异常而失败。
    • @supercat:ISortableList&lt;T&gt; 可以提供哪些IList&lt;T&gt; 中还没有的东西?或者,换种说法,为什么不能在不通过您想象的静态方法重新添加项目的情况下对IList&lt;T&gt; 进行就地排序?
    • @ORMapper:如果列表使用数组作为后备存储(常见,但不是必需的),则直接访问数组元素的排序例程可能比必须通过IList&lt;T&gt; 访问每个元素的接口。速度差异非常大,以至于在许多情况下,将列表复制到数组、对数组进行排序、然后将列表复制回来可能比尝试让排序例程处理列表要快。
    • ComparisonComparer 类不是必需的。您可以改用标准静态方法Comparer&lt;T&gt;.Create(comparison)
    【解决方案3】:

    使用 LINQ To Objects 为您排序怎么样?

    假设你有一个IList&lt;Car&gt;,而汽车有一个Engine属性,我相信你可以排序如下:

    from c in list
    orderby c.Engine
    select c;
    

    编辑:您确实需要快速在这里获得答案。由于我提出的其他答案的语法略有不同,因此我将留下我的答案 - 但是,提出的其他答案同样有效。

    【讨论】:

    • 它将创建一个新的枚举,这在某些情况下可能是不可取的。你不能通过接口对 IList 进行就地排序,除非我知道使用 ArrayList.Adapter 方法。
    【解决方案4】:

    您将不得不做一些我认为的事情(将其转换为更具体的类型)。

    也许将它放入 T 的列表而不是 ArrayList,这样您就可以获得类型安全和更多选项来实现比较器。

    【讨论】:

      【解决方案5】:

      @DavidMills 接受的答案非常好,但我认为可以改进。一方面,当框架已经包含静态方法Comparer&lt;T&gt;.Create(Comparison&lt;T&gt;) 时,无需定义ComparisonComparer&lt;T&gt; 类。此方法可用于即时创建IComparison

      此外,它会将IList&lt;T&gt; 转换为IList,这可能是危险的。在我看到的大多数情况下,实现IListList&lt;T&gt; 在幕后用于实现IList&lt;T&gt;,但这并不能保证并且会导致代码脆弱。

      最后,重载的List&lt;T&gt;.Sort()方法有4个签名,只实现了2个。

      1. List&lt;T&gt;.Sort()
      2. List&lt;T&gt;.Sort(Comparison&lt;T&gt;)
      3. List&lt;T&gt;.Sort(IComparer&lt;T&gt;)
      4. List&lt;T&gt;.Sort(Int32, Int32, IComparer&lt;T&gt;)

      下面的类实现了IList&lt;T&gt;接口的所有4个List&lt;T&gt;.Sort()签名:

      using System;
      using System.Collections.Generic;
      
      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<T>(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];
              }
          }
      }
      

      用法:

      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));
      }
      

      这里的想法是尽可能利用底层List&lt;T&gt; 的功能来处理排序。同样,我见过的大多数 IList&lt;T&gt; 实现都使用它。在底层集合是不同类型的情况下,回退到使用输入列表中的元素创建List&lt;T&gt; 的新实例,使用它进行排序,然后将结果复制回输入列表。即使输入列表没有实现IList 接口,这也会起作用。

      【讨论】:

        【解决方案6】:
        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));**
        

        【讨论】:

        • 如果你希望它是 DESC 而不是 ASC 怎么办?
        • @sambyte OrderByDescending 可能。我不确定这个答案是否符合要求
        【解决方案7】:

        在我寻找原始帖子中描述的确切问题的解决方案时发现了这个线程。然而,没有一个答案完全符合我的情况。布罗迪的回答非常接近。这是我找到的情况和解决方案。

        我有两个由 NHibernate 返回的相同类型的 IList,并且已经将两个 IList 合二为一,因此需要进行排序。

        就像 Brody 所说,我在对象 (ReportFormat) 上实现了 ICompare,这是我的 IList 的类型:

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

        然后我将合并后的 IList 转换为相同类型的数组:

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

        然后对数组进行排序:

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

        由于一维数组实现了接口System.Collections.Generic.IList&lt;T&gt;,所以可以像原来的IList一样使用数组。

        【讨论】:

          【解决方案8】:

          对于网格排序很有用,此方法根据属性名称对列表进行排序。如下示例。

              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();
                      }
                  }
              }
          

          【讨论】:

            【解决方案9】:

            这是一个使用强类型的示例。不确定这是否一定是最好的方法。

            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;
                }
            }
            

            Cast 函数只是对 3.5 附带的扩展方法的重新实现,编写为普通静态方法。不幸的是,它非常丑陋和冗长。

            【讨论】:

              【解决方案10】:

              在 VS2008 中,当我单击服务引用并选择“配置服务引用”时,有一个选项可以选择客户端如何反序列化从服务返回的列表。

              值得注意的是,我可以在 System.Array、System.Collections.ArrayList 和 System.Collections.Generic.List 之间进行选择

              【讨论】:

                【解决方案11】:
                using System.Linq;
                
                var yourList = SomeDAO.GetRandomThings();
                yourList.ToList().Sort( (thing, randomThing) => thing.CompareThisProperty.CompareTo( randomThing.CompareThisProperty ) );
                

                这很漂亮!贫民窟。

                【讨论】:

                  【解决方案12】:

                  找到了一篇关于此的好帖子,并认为我会分享。 Check it out HERE

                  基本上。

                  您可以创建以下类和 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);
                  }
                  }
                  

                  那么如果你有一个IList,你可以这样排序。

                  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());
                  

                  但请查看此网站以获取更多信息...Check it out HERE

                  【讨论】:

                    【解决方案13】:

                    这是一个有效的解决方案吗?

                            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();
                    

                    结果是: 列表 乙 一种 C

                    列表 一种 乙 C

                    再次列出 一种 乙 C

                    【讨论】:

                    • 如果它真的是一个 List 则有效。在某些情况下,您有其他实现 IList 的类型(例如,一个普通数组),而向下转换不起作用。太糟糕了,Sort() 方法不是 IList 的扩展方法。
                    【解决方案14】:

                    如果你问我,这看起来要简单得多。这对我来说非常有效。

                    您可以使用 Cast() 将其更改为 IList,然后使用 OrderBy():

                        var ordered = theIList.Cast<T>().OrderBy(e => e);
                    

                    WHERE T 是类型,例如。 Model.Employee 或 Plugin.ContactService.Shared.Contact

                    然后您可以使用 for 循环及其 DONE。

                      ObservableCollection<Plugin.ContactService.Shared.Contact> ContactItems= new ObservableCollection<Contact>();
                    
                        foreach (var item in ordered)
                        {
                           ContactItems.Add(item);
                        }
                    

                    【讨论】:

                      【解决方案15】:

                      将您的IList 转换为List&lt;T&gt; 或其他一些通用集合,然后您可以使用System.Linq 命名空间轻松查询/排序它(它将提供一堆扩展方法)

                      【讨论】:

                      • IList&lt;T&gt; 实现了IEnumerable&lt;T&gt;,因此不需要转换为使用Linq 操作。
                      猜你喜欢
                      • 2016-01-02
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-06-15
                      • 1970-01-01
                      • 2018-10-11
                      相关资源
                      最近更新 更多