确定性排序的对象列表

时间:2018-08-31 16:36:25

标签: c# sorting

我需要对对象列表和任意的sort属性进行排序。如果列表中有多个对象具有该sort属性的相同值,则重复对同一列表进行排序将重新排列具有相同sort属性值的成员。需要明确的是,每次运行排序时,都会生成一个新列表,并且成员的顺序是任意的,不一定与上次对列表进行排序时的顺序相同。有办法避免这种情况吗?

这是一个代码示例:

List<T> myList; // T is arbitrary and I doesn't implement any common interface
PropertyInfo sortPorperty = some property of T

var sortedList = myList.OrderBy(x => sortProperty.GetValue(x));

多次执行此代码将导致对象的顺序不同。

我最初的想法是也按对象本身进行排序

var sortedList = myList.OrderBy(x => sortProperty.GetValue(x)).ThenBy(x => x);

但是据我所知,它将按哈希码排序,并且基本上是对象的内存位置,因此两次运行之间不会相同。还有其他可行的方法吗?

1 个答案:

答案 0 :(得分:2)

如果类型是可序列化的,则可以使用对象的序列化作为最终的排序条件。

使用BinaryFormatter为对象生成一个唯一字符串(在此示例中,我将其称为 Idem )并将其用作最终的.ThenBy排序标准。

在此示例中,我将对象的二进制格式版本转换为base64字符串(可以肯定的是性能开销,还有其他方法可以使二进制版本很好地进行比较,但是我只是在说明一般方法)

我认为这个例子包含了您所要求的一切。没有接口的任意类型,使用属性作为OrderBy标准,并且不依赖于项目的初始顺序来在后续运行中产生相同的输出顺序。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;

public static class Extensions
{
    // Returns a string unique(TM) to this object.
    public static string Idem<T>(this T toSerialize)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        var memoryStream = new MemoryStream();
        using (memoryStream)
        {
            formatter.Serialize(memoryStream, toSerialize);
            return Convert.ToBase64String(memoryStream.ToArray());
        }
    }
}

[Serializable()]
public class Person
{
    public Person(string name, string secret)
    {
        this.name = name;
        this.secret = secret;
    }

    private string secret; // some private info
    public string Nickname { get { return name; } } // some property
    public string name; // some public info

    public override string ToString() // a way to see the private info for this demo
    {
        return string.Format("{0} ({1})", name, secret);
    }
}

class Program
{
    static void Main(string[] args)
    {
        // You can rearrange the items in this list and they will always come out in the same order.
        List<Person> myList = new List<Person>() {
            new Person("Bob", "alpha"),
            new Person("Bob", "bravo"),
            new Person("Alice", "delta"),
            new Person("Bob", "echo"),
            new Person("Bob", "golf"),
            new Person("Bob", "foxtrot"),
        };
        PropertyInfo sortProperty = typeof(Person).GetProperty("Nickname");

        Random random = new Random();
        for (int i = 0; i < 3; ++i)
        {
            var randomList = myList.OrderBy(x => random.Next());


            var sortedList = randomList.OrderBy(x => sortProperty.GetValue(x))
                .ThenBy(x => x.Idem()); // Here's the magic "Then By Idem" clause.

            Console.WriteLine(string.Join(Environment.NewLine, sortedList));
            Console.WriteLine();
        }
    }
}