【问题标题】:What is the best way to sort an IList<T> in .Net 2.0?在 .Net 2.0 中对 IList<T> 进行排序的最佳方法是什么?
【发布时间】:2025-12-13 11:45:01
【问题描述】:

我有一个需要排序的IList&lt;T&gt;,如果可能的话,我宁愿不复制该列表。我注意到ArrayList 有一个Adapter 静态方法,它包装传递的列表而不复制它,但这需要一个IList,而我有一个IList&lt;T&gt;。从System.Collections.Generic.IList&lt;T&gt; 转换为System.Collections.IList 并仅使用Adapter 方法是否安全?

请注意,这是 .Net 2.0,因此不能选择 LINQ。

【问题讨论】:

标签: .net .net-2.0


【解决方案1】:

来自 Paul Fox 的博客,我推荐帖子“如何对 IList 进行排序”:http://foxsys.blogspot.com/2007/06/how-to-sort-generic-ilist.html

以防万一该博客将来消失,我将在此处复制帖子:


如何对通用 IList 进行排序

更新

您可以阅读和更新有关sorting generic IList and List 的帖子。许多人会更喜欢更新帖子中提到的方法。

对通用 IList 进行排序

我试图对一个通用的 IList 进行排序,并找到了一种相当简单的方法。

第一步

您需要为 IList 中包含的类型实现 IComparable。对于这个例子,我将使用一个简单的 Language 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
}

第 2 步

对您的 IList 进行排序。为此,您将使用传入 IList 的 ArrayList.Adapter() 方法,然后调用 Sort 方法。就这样……

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

注意:语言的类型是“IList”

语言应该是您类型的排序列表!

【讨论】:

  • 所以他只是将 IList 转换为 IList。为什么这是一个安全的演员阵容?
  • 不要复制整个帖子。确保内容得到保留似乎是正确的做法,但复制整个帖子可能需要版权所有者的明确许可。不过,您可以而且应该创建一个摘要。
  • @EddieDeyo:我知道这是几年前的事了,但这是因为 IList 并不真正存在,而是 LanguageDto 的 IList,当然是 LanguageDto 的 IList。 T (主要)是编译时错觉。 IList 和 IList 之间的“区别”在于 IList Item[0] 返回一个 Object,但 IList Item[0] 返回一个始终为 T 的 Object,如果 T 是一个结构/primitive 对象不会被装箱。因此,从 IList 到 IList 的强制转换是安全的(不必要,但安全),但反过来不是
【解决方案2】:

您不能将 IList(T) 转换为 IList。

在使用 Reflector 进行一些嗅探之后,似乎 ArrayList.Adapter(IList).Sort() 将首先将列表复制到对象数组,对数组进行排序,然后将数组复制回列表:

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

如果 List(T) 中的 T 是值类型,则可能会产生装箱开销。

如果您需要更改您拥有的列表中对象的顺序,您可以类似地进行:

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

如果列表太大(如数亿个项目)以至于您无法在内存中制作额外的副本,我建议首先使用 List(T) 或实现您最喜欢的就地排序算法。

【讨论】:

  • 是的,但我说过我希望避免复制列表。
  • 不要复制它,直接说:unsorted = sorted;
  • @Hallgrim:当然可以。 ideone.com/bqzIpk4IList x = new int[]{1,2,3, 4}; IList&lt;int&gt; g = (IList&lt;int&gt;) x; System.Console.WriteLine(g.Count); IList g2 = g as IList; System.Console.WriteLine(g2.Count);
【解决方案3】:

由于 IList 接口上没有 Sort 方法,您可以考虑创建自己的方法:

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

通常,您在方法中指定的参数类型应该是您实际需要调用的成员的最小公分母。如果您确实需要调用 Sort() 方法,那么您的参数应该定义该成员。否则,您可能应该将其加载到另一个可以执行您想要的操作的对象中,例如:

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

这实际上应该很快,几乎可以肯定比编写自己的自定义内联排序算法还要快。

【讨论】:

    【解决方案4】:

    我知道它不是 .NET 2.0,但我非常喜欢 LINQ,我会一有机会就支持它 :)

    简单排序:

    var sortedProducts =
        from p in products
        orderby p.ProductName
        select p;
    
    ObjectDumper.Write(sortedProducts);
    

    按多个条件排序:

    string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
    
    var sortedDigits =
        from d in digits 
        orderby d.Length, d
        select d;
    

    两个例子都来自101 Linq Samples

    【讨论】:

    • 这很好。通过 google 的魔力,很可能有人会来到这里寻求不共享 OP 2.0 约束的帮助。
    【解决方案5】:

    如果您需要对不同类的列表(而不是 IList)进行排序,而不需要为所有类创建单独的比较器类并且仍然保持实体类干净(您不想实现 IComparable),您可以使用以下(与 .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.");
        }
    }
    

    如果您使用 .NET 2.0(在 Replacing Func with delegates C# 上找到),您还需要 Func 委托:

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

    用法:

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

    一些简单的单元测试:

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

    【讨论】:

      【解决方案6】:
      IList<object> unsorted = ...
      IList<object> sortedList = unsorted.Orderby(x => x.Tostring()).Tolist();
      

      这将给出对象特定字段的排序列表。

      【讨论】:

        最近更新 更多