在C#中对IList进行排序


86

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

事实证明,IList接口没有内置的排序方法。

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

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

所以我的问题是,有人对IList进行排序是否有一种优雅的解决方案


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

Answers:


54

如何使用LINQ To Objects为您排序?

假设您有IList<Car>,并且汽车具有Engine属性,我相信您可以将其排序如下:

from c in list
orderby c.Engine
select c;

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


3
它将创建一个新的枚举,这在某些情况下可能是不希望的。您不能通过接口就地对IList <T>进行排序,除非在我所知的情况下使用ArrayList.Adapter方法。
Tanveer Badar 2014年

67

您可以使用LINQ:

using System.Linq;

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

61

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

我认为,理想情况下,.NET Framework将包括一个接受IList <T>的静态排序方法,但第二件事是创建自己的扩展方法。创建几个方法将使您能够像对List <T>一样对IList <T>进行排序并不是很困难。另外,您可以使用相同的技术来重载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/


1
正确的方法实际上应该是提供一个ISortableList<T>接口(提供使用某些特定比较器对列表的一部分进行排序的方法),List<T>实现该接口以及使用一种静态方法,该方法可以IList<T>通过检查接口是否实现ISortableList<T>以及是否进行排序来对任何列表进行排序。将其复制到数组,进行排序,清除IList<T>,然后重新添加项目。
supercat 2012年

4
很棒的答案!但是,请注意:此方法假定IList<T> list可以将其强制转换为非通用IList接口。如果您编写自己的实现IList<T>接口的类,请确保还实现非通用IList接口,否则代码将因类强制转换失败而失败。
斯坦(Sstan)2015年

1
@supercat:有什么可以ISortableList<T>提供的IList<T>呢?或者,以不同的方式问,为什么不IList<T>按您想象的静态方法重新添加项目就不能就地排序?
OR Mapper

@ORMapper:如果列表使用数组作为后备存储(常见但非必需),则直接访问数组元素的排序例程可能比必须通过IList<T>接口访问每个元素的排序例程快得多。速度差足够大,以致于在许多情况下,将列表复制到数组,对数组进行排序并复制回列表可能比尝试使用排序例程来处理列表要快。
超级猫

1
ComparisonComparer班是没有必要的。您可以改用标准静态方法Comparer<T>.Create(comparison)
linepogl

9

您将必须做我想的事情(将其转换为更具体的类型)。

也许将其放入T列表而不是ArrayList中,以便获得类型安全性以及实现比较器方式的更多选项。


4

@DavidMills接受的答案是相当不错的,但我认为可以对此进行改进。首先,ComparisonComparer<T>当框架已经包含静态方法时,无需定义类Comparer<T>.Create(Comparison<T>)。此方法可用于动态创建IComparison

此外,它转换IList<T>IList具有是危险的潜力。在我所见的大多数情况下,幕后使用了List<T>哪些工具IList来实施IList<T>,但这并不能保证并且会导致代码变脆。

最后,重载List<T>.Sort()方法具有4个签名,并且仅实现了2个。

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

下面的类List<T>.Sort()IList<T>接口实现所有4个签名:

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<T>来处理排序。同样,IList<T>我见过的大多数实现都使用此方法。如果基础集合是其​​他类型,则回List<T>退到使用输入列表中的元素创建新实例的后备,使用它进行排序,然后将结果复制回输入列表。即使输入列表未实现该IList接口,这也将起作用。


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

1

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

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

就像布罗迪说的那样,我在对象(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

由于一维数组实现了interface System.Collections.Generic.IList<T>,因此可以像原始IList一样使用该数组。


1

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

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

0

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

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扩展方法的重新实现,该扩展方法是作为常规静态方法编写的。不幸的是,它非常丑陋且冗长。


0

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

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


0
using System.Linq;

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

相当!贫民窟。


0

在此找到了很好的帖子,并认为我会分享。在这里查看

基本上。

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

但是请查看此网站以获取更多信息...在此处查看


0

这是有效的解决方案吗?

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

结果是:IList B A C

清单A B C

我再次列出A B C


1
如果它确实是List <T>,则有效。在某些情况下,您还有其他实现IList <T>的类型(例如,纯数组),其中向下转换不起作用。不幸的是,Sort()方法不是IList <T>的扩展方法。
Cygon 2012年

0

如果您问我,这看起来更加简单。这完全适合我。

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

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

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

-1

将您转换IListList<T>或其他通用集合,然后可以使用System.Linq名称空间轻松地对其进行查询/排序(它将提供一堆扩展方法)


9
IList<T>实现IEnumerable<T>,因此不需要转换为使用Linq操作。
史蒂夫·吉迪
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.