C# OrderBy的效率和延迟执行

C# OrderBy的效率和延迟执行,c#,linq,C#,Linq,我有一个包含日期和值的对象列表。每个日期有一个对象,过去几个月的每个日期都有一个对象。我正在查找值更改为最新值的日期 以下是我的意思的一个例子: <datevalue> <date>8-9</date> <value>5</value> </datevalue> <datevalue> <date>8-10</date> <value>6<

我有一个包含日期和值的对象列表。每个日期有一个对象,过去几个月的每个日期都有一个对象。我正在查找值更改为最新值的日期

以下是我的意思的一个例子:

<datevalue>
    <date>8-9</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-10</date>
    <value>6</value>
</datevalue>
<datevalue>
    <date>8-11</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-12</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-13</date>
    <value>5</value>
</datevalue>

8-9
5.
8-10
6.
8-11
5.
8-12
5.
8-13
5.
在上面的示例中,当前值为5,因为它是最近日期8-13的值。我想返回8-11 datevalue对象,因为它是值更改为最新值的日期。我不想要8-9的值,因为即使它是当前值最早的一天,该值在该日期之后更改

这是我第一次尝试解决这个问题:

DateValue FindMostRecentValueChange(List<DateValue> dateValues)
{
    var currentValue = dateValues
                        .OrderByDesc(d => d.date)
                        .Select(d => d.value)
                        .First();
    var mostRecentChange = dateValues
                            .OrderByDesc(d => d.date)
                            .TakeWhile(d => d.value = currentValue)
                            .Last();
    return mostRecentChange;
}
DateValue FindMostRecentValueChange(列出日期值)
{
var currentValue=日期值
.OrderByDesc(d=>d.date)
.选择(d=>d.value)
.First();
var mostRecentChange=dateValues
.OrderByDesc(d=>d.date)
.TakeWhile(d=>d.value=currentValue)
.Last();
返回最新的更改;
}
这很有效。然而,有人向我指出,我正在为这两个操作重复OrderByDesc。考虑到OrderByDesc可能是一个昂贵的操作,我不想做两次。因此,我做了一个改变:

DateValue FindMostRecentValueChange(List<DateValue> dateValues)
{
    var orderedDateValues = dateValues.OrderByDesc(d => d.date);
    var currentValue = orderedDateValues;
                        .Select(d => d.value)
                        .First();
    var mostRecentChange = orderedDateValues
                            .TakeWhile(d => d.value = currentValue)
                            .Last();
    return mostRecentChange;
}
DateValue FindMostRecentValueChange(列出日期值)
{
var orderedDateValues=dateValues.OrderByDesc(d=>d.date);
var currentValue=OrderedDateValue;
.选择(d=>d.value)
.First();
var mostRecentChange=orderedDateValues
.TakeWhile(d=>d.value=currentValue)
.Last();
返回最新的更改;
}
现在我只给OrderByDesc打过一次电话。这是一种进步,对吗?嗯,也许不是。OrderByDesc是一个延迟执行

据我所知,这意味着在你要求它提供一个值之前,实际的排序是不会完成的。因此,当您在查找currentValue时调用First()时,将执行OrderByDesc,然后在查找mostRecentChange时调用Last()时,将再次执行OrderByDesc。这是否意味着我仍在执行OrderByDesc两次


我是否正确解释了延迟执行的运作方式?我希望编译器能够识别这个场景,并在幕后对其进行优化,以便只调用一次执行,但我找不到任何信息来支持这个理论。您能帮我想一想优化此解决方案的最佳方法吗?

不,如果您使用
First()
Last()
和其他一些方法,您的查询将正确执行。这意味着您需要调用两次
OrderBy
(包括
OrderByDescending

您可以尝试以下方法:

var mostRecentChange = dateValues.OrderBy(d=>d.Date)
                                 .SkipWhile((x,i)=>i==dateValues.Count-1||x.Value == dateValues[i+1].Value)
                                 .Take(1);
这是否意味着我仍在执行OrderByDesc两次

是的,没错

我希望编译器能够识别这个场景,并在幕后对其进行优化,以便只调用一次执行,但我找不到任何信息来支持这个理论

它不能,因为这将在几个关键方面改变预期的功能

  • 如果基础数据发生了更改,则在再次迭代序列时应反映这些更改。如果在第一个查询和第二个查询之间向
    dateValues
    添加了一个新项,则该项应位于第二个查询中。如果你删除了一个项目,它不应该在那里,等等

  • 为了得到你想要的东西,它需要将所有的物品储存在某种集合中,即使在第一个消费者“用完”它们之后也是如此。这是不可取的。这里的想法是,您可以对数据进行流式处理,一旦处理完一个项目,您就“完成”了它,而不需要将其保存在内存中。如果没有足够的内存来保存查询中的所有项以供后续运行,该怎么办

  • 你能帮我想一想优化这个解决方案的最佳方法吗


    这很琐碎。只需用查询结果填充数据结构即可。最简单的方法就是把它们都列在一个列表中。在查询的末尾添加一个
    ToList
    调用,它将对其进行一次计算,然后可以对结果列表进行多次迭代,而不会产生负面后果。由于此解决方案在需要此类语义时非常容易获得,而延迟执行的语义更难获得,尽管功能更强大,但他们选择不将LINQ基于物化集合。

    延迟执行不是延迟执行-不重要……)确切地A
    ToList()后跟
    var mostRecentChange=sortedList.TakeWhile(d=>d.Value==sortedList[0].Value).Last()