9

I've come across a very weird behaviour of the .NET framework(s), when sorting a collection. This behaviour is different between .NET 3.5 and 4.0 (but I think I know why), but more importantly (and that's my real concern here), the behaviour is different accross different machines on the same framework.

Context

I'm working on a piece of software that's relying on some third party software (spring.net in this case but it doesn't matter), and at some point, it's sorting a collection that has all its item "equal" (the comparer always return 0). This is NOT under my control and I'd be very fine with it if the behaviour of sorting that list was always consistent. It's not.

How to reproduce

Create a simple project, in .NET 3.5, and run the code below. When compiled in 3.5, the behaviour seems to be consistent, and the collection will be "reversed" (it comes out as Three, Two, One). Now, please change the project target to .NET 4 (not 4.5), and run it again: On my machine, it doesn't reverse the collection anymore (One, Two, Three), but on some other colleagues machine, it does (Three, Two, One)!!! We have exactly the same setup...

Can you please tell me, on your machine, under 4.0, which it is? Reversed or not reversed?

I'm trying to assess whether my setup is correct, or not.

Proof of concept

class Program
{
    static void Main()
    {
        var collection = new ArrayList
        {
            "One",
            "Two",
            "Three",
        };

        // It should in any case write One, Two, Three
        Console.Out.WriteLine("Before sort: ");
        foreach (string item in collection)
        {
            Console.Out.WriteLine("\t"+item);
        }

        collection.Sort(new OrderComparator());

        // In .NET 3.5, it will write Three, Two, One
        // In .NET 4, it will sometimes write Three, Two, One, sometimes One, Two, Three: what is it for you?
        Console.Out.WriteLine("After sort: ");
        foreach (string item in collection)
        {
            Console.Out.WriteLine("\t" + item);
        }

        Console.Out.WriteLine("--end--");
        Console.Read();
    }
}

public class OrderComparator : IComparer
{
    public virtual int Compare(object o1, object o2)
    {
        return 0;
    }
}

Also, if you have any idea why this is happening, please let me know!

Community
  • 1
  • 1
Antoine Jaussoin
  • 5,002
  • 4
  • 28
  • 39
  • 2
    Like he said, the sorting is being done in third-party code that can't be changed. – DOK Mar 11 '13 at 12:20
  • For info: LINQ's sort ***is*** "stable" - but is not an in-place sort – Marc Gravell Mar 11 '13 at 12:23
  • The sorting algorithm was changed in .NET 4. This is a GIGO problem, garbage in, garbage out. Write a proper comparer. – Hans Passant Mar 11 '13 at 12:39
  • As I said the problem is I don't control any of that (the sorting, etc.). It's basically a bug in Spring.net, that (by luck) didn't cause any issue while under 3.5 but started to cause us headaches when switching to 4.0. It seems the best course of action would be to fix spring.net... – Antoine Jaussoin Mar 11 '13 at 12:45
  • Actually I'll amend my previous comment, the bug wasn't in Spring.net (even though Spring.net should have complained), but in one of our config file where we didn't set the Order property. Anyway, all solved :) – Antoine Jaussoin Mar 12 '13 at 09:55

1 Answers1

9

The sort done by ArrayList.Sort() is not stable, therefore you cannot predict the order in which "identical" items will sort.

Further, because ArrayList.Sort() may use a random mechanism to select the pivot for its QuickSort algorithm, identical items may be sorted differently on different PCs or even on the same PC.

[EDIT: I can't find any evidence for a random pivot being chosen in current implementations, but the array sort is still unstable. I'm guessing the randomness comes from the native code Quicksort implementation in TrySZSort() which is what probably gets called.]

Also for interest's sake, Reflector shows this code in the ArrayList.Sort() (if you dig in a bit):

internal void Sort(int left, int length)
{
    if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
    {
        this.IntrospectiveSort(left, length);
    }
    else
    {
        this.DepthLimitedQuickSort(left, (length + left) - 0x1, 0x20);
    }
}

which seems to be selecting an entirely different sort algorithm for .Net 4.5.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • That could be it: it seems that the difference between our machines is that I have VS 2012, and the other users don't (only 2010). Apparently installing VS 2012 replaces some DLL of .NET 4, and probably changes the sort algorithm to the Introspective Soft as you mentioned earlier. Thanks! – Antoine Jaussoin Mar 11 '13 at 14:08
  • Thank you for adding the Reflector(ed) code showing IntrospectiveSort versus DepthLimitedQuickSort. That was a big help to me. – cplotts Sep 15 '17 at 13:58