在C#中对IList进行排序

lom*_*axx 85 c# sorting generics ilist

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

结果是IList接口没有内置的排序方法.

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

我玩弄了一个扩展方法,也继承了IList并实现了我自己的Sort()方法以及转换为List,但这些方法似乎都不太优雅.

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

Mar*_*ade 63

您可以使用LINQ:

using System.Linq;

IList<Foo> list = new List<Foo>();
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar);
IList<Foo> sortedList = sortedEnum.ToList();
Run Code Online (Sandbox Code Playgroud)


Dav*_*lls 56

这个问题激发了我写博客的一篇文章: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));
    }

    // 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这些扩展,像对待List一样对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));
Run Code Online (Sandbox Code Playgroud)

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

  • 精彩的回答!但是,需要注意的是:这种方法假定`IList <T> list`可以强制转换为非泛型`IList`接口.如果您编写自己的类来实现`IList <T>`接口,请确保您还实现了非泛型`IList`接口,否则代码将因类抛出异常而失败. (3认同)
  • 正确的方法实际上是提供一个“ISortableList&lt;T&gt;”接口(使用某些特定比较器对列表的一部分进行排序的方法),让“List&lt;T&gt;”实现它,并有一个静态方法可以通过检查任何“IList&lt;T&gt;”是否实现了“ISortableList&lt;T&gt;”来对其进行排序,如果没有,则将其复制到数组中,对其进行排序,清除“IList&lt;T&gt;”,然后重新添加项目。 (2认同)
  • `ComparisonComparer` 类不是必需的。您可以使用标准静态方法“Comparer&lt;T&gt;.Create(comparison)”来代替。 (2认同)

Bra*_*ach 54

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

假设你有IList<Car>,并且汽车Engine有财产,我相信你可以按如下方式排序:

from c in list
orderby c.Engine
select c;
Run Code Online (Sandbox Code Playgroud)

编辑:你需要快速在这里得到答案.由于我提出了与其他答案略有不同的语法,我将留下我的答案 - 但是,所提出的其他答案同样有效.

  • 它将创建一个新的可枚举,这在某些情况下可能是不可取的。根据我的知识,除非使用 ArrayList.Adapter 方法,否则您无法通过接口就地对 IList&lt;T&gt; 进行排序。 (3认同)

Leo*_*ick 9

你将不得不做我认为的事情(将其转换为更具体的类型).

也许把它放到一个T而不是ArrayList的列表中,这样你就可以获得类型安全性和更多选项来实现比较器.


dan*_*ana 6

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

此外,它所施放的东西IList<T>IList有潜在的危险。在我见过的大多数情况下,List<T>which ImplementsIList是在幕后使用来实现的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> listImpl)
        {
            listImpl.Sort();
        }
        else
        {
            var 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> listImpl)
        {
            listImpl.Sort(comparison);
        }
        else
        {
            var 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> listImpl)
        {
            listImpl.Sort(comparer);
        }
        else
        {
            var 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> listImpl)
        {
            listImpl.Sort(index, count, comparer);
        }
        else
        {
            var 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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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));
}
Run Code Online (Sandbox Code Playgroud)

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