问答中心分类: LIST如何对列表进行排序<T>通过对象中的属性
0
匿名用户 提问 17分钟 前

我有一堂课叫Order它具有诸如OrderId,OrderDate,Quantity, 和Total.我有这个清单Order班级:

List objListOrder = new List();
GetOrderList(objListOrder); // fill list of orders

我想根据一个属性对列表进行排序Order目的;例如,按订单日期或订单 ID。
我怎样才能在 C# 中做到这一点?

22 Answers
0
LukeH 回答 17分钟 前

如果您需要就地对列表进行排序,则可以使用Sort方法,传递一个Comparison<T>代表:

objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

如果您更喜欢创建一个新的排序序列而不是就地排序,那么您可以使用 LINQOrderBy方法,如其他答案中所述。

Jon Schneider 回复 17分钟 前

如果您的日期字段是类型可空的日期时间(即日期时间?),你会得到一个编译错误,比如“无法将 lambda 表达式转换为类型 ‘System.Collections.Generic.IComparer’ 因为它不是委托类型”,真实的问题可能是可空类型 DateTime? 上没有 CompareTo 方法。如果您确定该对象不会为空,则可以将其转换为纯 DateTime 以解决问题。

Jonathan Wood 回复 17分钟 前

是的,这是“正确”的答案,应该比创建新的 IEnumerable 然后将其转换为新列表更有效。

Jeppe Stig Nielsen 回复 17分钟 前

当然,如果你需要降序排序,交换xy在箭头的右侧=>.

Mitchell Currie 回复 17分钟 前

真正对列表进行就地排序的真正答案

Cdaragorn 回复 17分钟 前

@PimBrouwers 这是更好的选择期。即使您使用的内存量不是问题,此解决方案也可以避免不必要的内存分配,这非常昂贵。这个选项在代码方面同样简单,而且速度快了一个数量级。

Jeppe Stig Nielsen 回复 17分钟 前

@JonSchneider 为Nullable<>(拿DateTime?作为例子)你可以使用.Sort((x, y) => Nullable.Compare(x.OrderDate, y.OrderDate))这会将 null 视为所有非 null 值之前的值。它与.Sort((x, y) => Comparer<DateTime?>.Default.Compare(x.OrderDate, y.OrderDate).

Luke Dupin 回复 17分钟 前

非常挑剔,但使用 a,b 而不是 x,y 更常见于排序。

Chris Bordeman 回复 17分钟 前

除非您调用 .ToList() 或 .ToArray(),否则使用 .OrderBy() 和 .ThenBy()(对于第二个字段)不会创建新列表,而只是一个迭代器。当您不想创建新列表时,它们实际上比排序更有效。

Dmitry Petelko 回复 17分钟 前

result.Sort((x, y) => String.Compare(x.Name, y.Name, StringComparison.Ordinal));如果您比较字符串字段,这对我来说没问题。祝你好运。

PerpetualStudent 回复 17分钟 前

请注意,根据文档,此方法是不稳定, 然而OrderBy稳定的.此外,对于列表Sort一个实例方法,而对于数组它是静态的。我可能会补充说,所有这些在语法方面都是不必要的混乱。

0
djdd87 回答 17分钟 前

要在 .Net2.0 上不使用 LINQ 执行此操作:

List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
    delegate(Order p1, Order p2)
    {
        return p1.OrderDate.CompareTo(p2.OrderDate);
    }
);

如果您使用的是 .Net3.0,那么 LukeH 的回答是你所追求的。
要对多个属性进行排序,您仍然可以在委托中进行。例如:

orderList.Sort(
    delegate(Order p1, Order p2)
    {
        int compareDate = p1.Date.CompareTo(p2.Date);
        if (compareDate == 0)
        {
            return p2.OrderID.CompareTo(p1.OrderID);
        }
        return compareDate;
    }
);

这会给你上升日期与下降订单 ID。
但是,我不建议坚持代表,因为这意味着很多地方没有代码重用。你应该实施一个IComparer然后把它传递给你的Sort方法。看这里.

public class MyOrderingClass : IComparer<Order>
{
    public int Compare(Order x, Order y)
    {
        int compareDate = x.Date.CompareTo(y.Date);
        if (compareDate == 0)
        {
            return x.OrderID.CompareTo(y.OrderID);
        }
        return compareDate;
    }
}

然后要使用这个 IComparer 类,只需实例化它并将其传递给您的 Sort 方法:

IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);
Jeb 回复 17分钟 前

很好的答案,应该是正确的答案,因为它可以保护重新初始化原始列表(LINQ 版本将始终这样做)提供更好的封装。

radarbob 回复 17分钟 前

@馄饨,不。这个想法是能够有不同的IComparer实现,给我们多态行为。

0
PSK 回答 17分钟 前

订购列表的最简单方法是使用OrderBy

List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ToList();

如果您想按多列排序,例如以下 SQL 查询。

ORDER BY OrderDate, OrderId

为此,您可以使用ThenBy喜欢以下。

List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
0
Jimmy Hoffa 回答 17分钟 前

如您所说,在没有 Linq 的情况下这样做:

public class Order : IComparable
{
    public DateTime OrderDate { get; set; }
    public int OrderId { get; set; }

    public int CompareTo(object obj)
    {
        Order orderToCompare = obj as Order;
        if (orderToCompare.OrderDate < OrderDate || orderToCompare.OrderId < OrderId)
        {
            return 1;
        }
        if (orderToCompare.OrderDate > OrderDate || orderToCompare.OrderId > OrderId)
        {
            return -1;
        }

        // The orders are equivalent.
        return 0;
    }
}

然后只需在您的订单列表中调用 .sort()

radarbob 回复 17分钟 前

必须首先测试nullas投掷。这是重点as, 如 (ha, ha)(Order)obj失败时抛出异常。if(orderToCompare == null) return 1;.

AeonOfTime 回复 17分钟 前

+1 用于使用接口,这使代码更易于维护并清楚地公开对象的功能。

radarbob 回复 17分钟 前

如果比尔和泰德出现,我会请他们带我回到 2014 年 10 月 16 日,这样我就可以纠正我上面的错误 –as返回null如果演员表失败。但至少空测试是正确的。

Jimmy Hoffa 回复 17分钟 前

@radarbob 是的.. 哎呀。 :) 但!此函数旨在通过对列表进行排序自动使用,该列表是List<Order>所以应该保证类型匹配as所以 2014 年你可能没有写错误,只是避免了不必要的保护声明:)

radarbob 回复 17分钟 前

应该保证有趣的一点。如果它被很好地封装以至于它不能被调用,除非传递一个List<Order>;但是你和我都遇到过自我封装的程序员,他不言而喻的假设是“我正在编写这段代码,所以它不会被错误使用”

0
radarbob 回答 17分钟 前

经典的面向对象解决方案
首先,我必须为 LINQ 的威力而跪下……现在我们已经解决了这个问题
JimmyHoffa 答案的变体。使用泛型CompareTo参数变得类型安全。

public class Order : IComparable<Order> {

    public int CompareTo( Order that ) {
        if ( that == null ) return 1;
        if ( this.OrderDate > that.OrderDate) return 1;
        if ( this.OrderDate < that.OrderDate) return -1;
        return 0;
    }
}

// in the client code
// assume myOrders is a populated List<Order>
myOrders.Sort();

这个默认的可排序性当然是可重用的。也就是说,每个客户端都不必冗余地重写排序逻辑。交换“1”和“-1”(或逻辑运算符,您的选择)会反转排序顺序。

Loc Huynh 回复 17分钟 前

对列表中的对象进行排序的简单方法。但我不明白你为什么返回 1 if (that == null)?

radarbob 回复 17分钟 前

它的意思是this对象大于空。出于排序的目的,空对象引用“小于”this目的。这就是我决定定义空值如何排序的方式。