我在面向.NET 4.0的项目中进行了以下测试:
[TestFixture]
public class Donkey
{
[Test]
public void TestListSorting()
{
var expected = new[]
{
MockRepository.GenerateStub<IComparable>(),
MockRepository.GenerateStub<IComparable>()
};
var sorted = new List<IComparable>(expected);
CollectionAssert.AreEqual(expected, sorted);
sorted.Sort();
CollectionAssert.AreEqual(expected, sorted);
}
}
如果我在仅安装了.NET 4.0的计算机上运行它,则会失败。 如果我在只安装了.NET 4.5的机器上运行它,它会通过。
我假设在.NET 4.5中,Sort
的实现已更改为在排序每个从0
返回CompareTo
的对象列表时维护顺序。
现在,抛开这个测试明显的疯狂。我知道依靠这种行为是疯狂的。
当然这是一个突破性的变化? this page没有列出.NET 4.0和4.5之间的兼容性。
这有什么理由吗?我错过了什么吗?是否有其他页面显示实际的重大变化?我应该坐下来停止恐慌吗?
答案 0 :(得分:35)
我之前已经回复了similar question。排序方法已在4.5到4.0之间进行了更改,从快速排序到introspective sort。
它实际上更快,但它仍然不是一个稳定的排序 1 ,也就是说,通过保留相等项的顺序,每次执行具有相同的输出。由于List.Sort
的实现不是一个稳定的类型,我认为你没有足够的时间运行你的单元测试,以便在两个运行时都出错?
我尝试使用等效代码和返回0的比较器自行重现它。有时候,在.NET 4.5和.NET 3.5中,列表顺序都会保留,有时则不会。
即使它确实将排序类型从稳定更改为不稳定,也不会发生重大变化。使用的排序类型和确切输出不是List.Sort
合同的一部分。方法合同保证的所有内容都是根据使用的比较器,您的项目将按排序顺序排列。
1 根据使用QuickSort
和HeapSort
的混合定义,它应该是不稳定的排序。算法的设计者David Musser甚至在his paper中说明了:
像快速排序一样,Introsort不稳定 - 不保留等效元素的顺序 - 所以仍然需要对稳定的排序例程有单独的要求。
答案 1 :(得分:5)
List.Sort
,The spec表示使用的种类不稳定,因此可能无法保留相等元素的排序;它没有指定特定对相等元素的重新排序,因此您无法真正将此更改称为重大更改。
答案 2 :(得分:3)
正如@Rawling所说,看看the documentation for Sort()
:
此实现执行不稳定的排序;也就是说,如果两个元素相等,则可能不会保留它们的顺序。
因此,您正在尝试测试明确记录为未定义的内容。这不是一个突破性的变化。
答案 3 :(得分:2)
我没有看到任何变化。正如其他人已经写过的那样,两个版本执行不稳定的排这意味着您不能依赖于等于比较的元素的顺序。他们的订单在排序期间可能会也可能不会改变这当然不是一个突破性的变化。
答案 4 :(得分:1)
答案 5 :(得分:0)
这样的问题经常会出现新的框架版本。有一些用于3.5→4.0过渡here和here。
对于版本的这种特殊更改,正如您的问题所示,差异已经出现了两个元素的数组或List<>
。另一个简单的例子是:
using System;
using System.Linq;
namespace SortTest
{
static class Program
{
static void Main()
{
var arr = new[] { new { Name = "Mary", Age = 17, }, new { Name = "Louise", Age = 17, }, };
Array.Sort(arr, (x, y) => x.Age.CompareTo(y.Age));
Console.WriteLine(string.Join(",", arr.Select(x => x.Name)));
}
}
}
使用.NET 4.0,它会打印Louise,Mary
。元素被交换。但是,使用.NET 4.5,它会打印Mary,Louise
。请注意,这两个女孩的年龄相同。
List<>.Sort
实例方法和Array.Sort
静态方法记录是不稳定的排序。他们可以按照他们想要的任何顺序自由地留下相同“大小”的元素。因此,您的代码不得对等效元素的顺序进行任何假设。
相比之下,Linq的OrderBy
方法执行稳定的排序。所以
var ordered = arr.OrderBy(x => x.Age);
鉴于他们具有相同的Age
,是 交换Mary和Louise。