C#排序多个值而不事先知道类型

时间:2016-05-20 23:33:09

标签: c#

以下代码按object.ToString()进行排序,因此我认为其工作原理不是。

我的问题是,是否有办法实现排序多值行列表(未知列类型)的目标。

我只需要支持int,double,string,DateTime和TimeSpan。排序总是升序。

DataTable table = new DataTable("TableFu") 
{ Columns = { "Name", "Age", "Grade", "BirthDay", "HowLong" } };

table.Rows.Add("Abe", 2, 1.3, new DateTime(2016, 1, 13), new TimeSpan(0, 30, 30));
table.Rows.Add("Abe", 1, 2.3, new DateTime(2016, 10, 13), new TimeSpan(1, 30, 30));
table.Rows.Add("Abe", 1, 2.3, new DateTime(2016, 2, 13), new TimeSpan(1, 30, 30));
table.Rows.Add("Abe", 1, 1.3, new DateTime(2016, 1, 13), new TimeSpan(1, 30, 30));
table.Rows.Add("Abe", 10, 1.3, new DateTime(2016, 1, 13), new TimeSpan(2, 30, 30));
table.Rows.Add("Abe", 1, 1.3, new DateTime(2016, 1, 13), new TimeSpan(1, 35, 30));
table.Rows.Add("Betty", 1, 2.3, new DateTime(2016, 1, 13), new TimeSpan(1, 30, 30));
table.Rows.Add("Betty", 1, 2.4, new DateTime(2014, 1, 13), new TimeSpan(1, 30, 30));
table.Rows.Add("Betty", 1, 10.4, new DateTime(2015, 1, 13), new TimeSpan(1, 30, 30));

using (DataView view = new DataView(table)) {
    view.Sort = "Name,Age,Grade,BirthDay,HowLong";
    DataTable result = view.ToTable();  //sorted by those columns in Ascending order
}

enter image description here

Per @InBetween和@PeterDuniho,这就是我的想法。

class ListComparer : IComparer<List<object>> {
    int IComparer<List<object>>.Compare(List<object> x, List<object> y) {
        return CompareList(x, y);
    }

    private int CompareList(List<object> first, List<object> other) {
        for (int i = 0; i < first.Count; ++i) {
            var firstitem = first[i] as IComparable;
            var otheritem = other[i] as IComparable;
            if (firstitem == null)
                throw new Exception("first item does not implement IComparable");
            if (otheritem == null)
                throw new Exception("other item does not implement IComparable");
            if (firstitem.CompareTo(otheritem) != 0) {
                return firstitem.CompareTo(otheritem);
            }
        }
        return 0;
    }
}

static void Main(string[] args) {

    if (1 as IComparable != null) Console.WriteLine("integer is IComparable");
    if (2.3 as IComparable != null) Console.WriteLine("double is IComparable");
    if ("a string" as IComparable != null) Console.WriteLine("string is IComparable");
    if (new DateTime(2016, 10, 30) as IComparable != null) Console.WriteLine("DateTime is IComparable");
    if (new TimeSpan(10, 23, 16) as IComparable != null) Console.WriteLine("TimeSpan is IComparable");

    List<List<Object>> list = new List<List<object>>();
    list.Add(new List<object>() { 2, 3.5, new DateTime(2016, 10, 10) });
    list.Add(new List<object>() { 1, 3.5, new DateTime(2016, 1, 1) });
    list.Add(new List<object>() { 10, 3.5, new DateTime(2016, 1, 1) });
    list.Add(new List<object>() { 2, 10.5, new DateTime(2016, 1, 1) });
    list.Add(new List<object>() { 1, 3.6, new DateTime(2016, 1, 1) });
    list.Add(new List<object>() { 10, 3.4, new DateTime(2016, 1, 1) });
    list.Add(new List<object>() { 2, 3.5, new DateTime(2016, 3, 10) });
    list.Add(new List<object>() { 1, 3.5, new DateTime(2016, 1, 1) });
    list.Add(new List<object>() { 10, 3.5, new DateTime(2016, 1, 1) });

    Console.WriteLine("\nUnsorted list of objects\n");
    foreach (var sublist in list) {
        foreach (var item in sublist) {
            Console.Write(item + "\t\t");
        }
        Console.WriteLine();
    }

    list.Sort(new ListComparer());

    Console.WriteLine("\nSorted list of objects\n");

    foreach (var sublist in list) {
        foreach (var item in sublist) {
            Console.Write(item + "\t\t");
        }
        Console.WriteLine();
    }


    Console.ReadLine();
}

enter image description here

1 个答案:

答案 0 :(得分:1)

您看到的顺序并不是您认为正在发生的顺序。视图不是按对象的实际类型进行排序,它只是按对象的字符串表示排序,即object.ToString()返回的内容。 / p>

一种简单的方法是在第二个示例中添加一个名为10的行。您将获得的排序不是数字11,...,22,...,10。您将获得的是11,......,102,...

所以这可能不是你想要的。

作为指向posible解决方案的指针,Enumerable.OrderBy的工作方式是,对于任何Enumerable<T>,它检查类型是否实现IComparable<T>IComparable(按此顺序)并使用相应的CompareTo实现。

有趣的是,如果是IEnumerable<object>,它只会检查非通用IComparable。以下内容不起作用:

class MyComparable: IComparable<MyComparable> { ... }
var objectList = new List<object>() { myComparable1, myComparable2, ... };
var ordered = objectList.OrderBy(o => o); //Throws, MyComparable does not impement `IComparable`

这种行为的原因是没有简单或合理的方法来确定具有未知类型的对象是否实现IComparable<himself>然后利用该信息:

 var t = o.GetType();
 var genericIComparableOfItself = t.GetInterfaces()
                                   .Where(i => i.IsGenericType && 
                                               i.GetGenericTypeDefinition().IsAssignableFrom(typeof(IComparable<>)) &&
                                               i.GetGenericArguments().First() == t)
                                   .FirstOrDefault();

 Func<object, int> compareTo = other => (int)comp.GetMethod("CompareTo", new Type[] { t }).Invoke(o, new object[] { other }); //yuck!

这是在实施IComparable时实施IComparable<T>始终是个好主意的一个原因。