在这种情况下,IEnumerable 被订购了多少次?
How many times is ordered the IEnumerable in this case?
我知道 IEnumerable<T>
在调用之前不会迭代。
假设我有这个代码:
foreach(int iteratorInt in myIEnumerable.OrderBy(x => x))
{
if(iteratorInt == myIEnumerable.First())
{
// do something
}
}
在 if
中,我正在检查第一个元素,因此每次迭代都必须对 myIEnumerable
进行排序,以查看哪个是第一个元素,或者它只被排序一次?
对于此代码:
foreach(int iteratorInt in myIEnumerable.OrderBy(x => x.MyProperty))
OrderBy
只执行一次
您的 Enumerable 将只订购一次,此处:myIEnumerable.OrderBy(x => x)
在这一行 if(iteratorInt == myIEnumerable.First())
将不会再次订购。
可能你误解了IEnumerable.First
方法,IEnumerable.First
和IEnumerable.OrderBy
方法没有任何关系。
Console.WriteLine(new [] {3, 2, 1}.First());
// here you get 3, not 1.
你可以在这里看到自定义的OrderBy
方法:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var myIEnumerable = GetMyEnumerable();
foreach(var item in myIEnumerable.MyCustomOrderBy(x => x))
{
if(item == myIEnumerable.First())
{
Console.WriteLine("The condition is true with: " + item);
}
}
}
public static IEnumerable<int> GetMyEnumerable()
{
foreach(var i in new int[] {5, 4, 3, 2, 1})
{
Console.WriteLine("GetMyEnumerable was called " + i);
yield return i;
}
}
}
public static class OrderByExtensionMethod
{
public static IOrderedEnumerable<TSource> MyCustomOrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
Console.WriteLine("OrderByExtensionMethod was called");
return source.OrderBy(keySelector);
}
}
输出:
OrderByExtensionMethod was called
GetMyEnumerable was called 5
GetMyEnumerable was called 4
GetMyEnumerable was called 3
GetMyEnumerable was called 2
GetMyEnumerable was called 1
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
The condition is true with: 5
发生了什么事?
首先调用了MyCustomOrderBy
方法,他需要遍历整个集合,对元素进行排序。
OrderByExtensionMethod was called
GetMyEnumerable was called 5
GetMyEnumerable was called 4
GetMyEnumerable was called 3
GetMyEnumerable was called 2
GetMyEnumerable was called 1
然后你的foreach开始,myIEnumerable.First()
对每一项执行:
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
终于如愿以偿了:
The condition is true with: 5
OrderBy
只计算一次,然而,每次迭代都会在原来无序myIEnumerable
的基础上创建一个新的IEnumerator<T>
,它可能不会匹配iteratorInt
在第一次迭代时,除非第一个元素恰好是将被排序到第一个位置的元素。
如果您做希望 iteratorInt
的第一次迭代值与可枚举的 First()
结果相匹配。您可能希望在循环 之前创建有序可枚举 的临时副本,如下所示:
var list = myIEnumerable.OrderBy(x => x);
foreach(int iteratorInt in list)
{
if(iteratorInt == list.First())
{
// do something
}
}
尽管这是一个毫无意义的模式(类似于“Loop-switch”反模式),因为它可以简化为:
//do something with list.First();
使用 LINQ 扩展时,查询只会在请求时执行,否则称为延迟执行。当多次请求同一个查询时,每次都会重新评估基础查询,除非初始查询已通过 .ToArrary()
或 .ToList()
.
之类的内容具体化
问题还不是很清楚,所以我将提供一些示例来演示各种行为。
示例 1:
- 将初始请求设置为局部变量。
- 在 foreach 中应用 LINQ 查询来订购集合。
- 使用初始局部变量查找第一个结果。
- 不要实现任何结果。
代码:
private static void Ex1()
{
Console.WriteLine("A");
IEnumerable<int> myIEnumerable = GetEnumerable();
Console.WriteLine("B");
foreach (int i in myIEnumerable.OrderBy(x => x))
{
Console.WriteLine("*** foreach : " + i);
if (i == myIEnumerable.First())
{
Console.WriteLine("=== Matched .First() : " + i);
}
}
Console.WriteLine("C");
}
示例 2:
- 将初始请求设置为局部变量。
- 在 foreach 外部应用 LINQ 查询以在不具体化结果的情况下对集合进行排序。
- 使用有序查询查找第一个结果
- 不要实现任何结果。
代码:
private static void Ex2()
{
Console.WriteLine("A");
IEnumerable<int> myIEnumerable = GetEnumerable();
Console.WriteLine("B");
var ordered = myIEnumerable.OrderBy(x => x);
foreach (int i in ordered)
{
Console.WriteLine("*** foreach : " + i);
if (i == ordered.First())
{
Console.WriteLine("=== Matched .First() : " + i);
}
}
Console.WriteLine("C");
}
示例 3:
- 将初始请求设置为局部变量。
- 在 foreach 外部应用 LINQ 查询来排序集合并具体化结果。
- 使用有序查询查找第一个结果。
代码:
private static void Ex3()
{
Console.WriteLine("A");
IEnumerable<int> myIEnumerable = GetEnumerable();
Console.WriteLine("B");
var ordered = myIEnumerable.OrderBy(x => x).ToArray();
foreach (int i in ordered)
{
Console.WriteLine("*** foreach : " + i);
if (i == ordered.First())
{
Console.WriteLine("=== Matched .First() : " + i);
}
}
Console.WriteLine("C");
}
所有查询都使用相同的方法来获取可枚举:
private static IEnumerable<int> GetEnumerable()
{
Console.WriteLine("~~~ GetEnumerable Start");
foreach (int i in new[]{3, 2, 1})
{
Console.WriteLine(">>> yield return : " + i);
yield return i;
}
Console.WriteLine("~~~ GetEnumerable End");
}
最终结果为:
====================
Ex A
====================
A
B
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
*** foreach : 1
~~~ GetEnumerable Start
>>> yield return : 3
*** foreach : 2
~~~ GetEnumerable Start
>>> yield return : 3
*** foreach : 3
~~~ GetEnumerable Start
>>> yield return : 3
=== Matched .First() : 3
C
====================
Ex B
====================
A
B
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
*** foreach : 1
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
=== Matched .First() : 1
*** foreach : 2
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
*** foreach : 3
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
C
====================
Ex C
====================
A
B
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
*** foreach : 1
=== Matched .First() : 1
*** foreach : 2
*** foreach : 3
C
我知道 IEnumerable<T>
在调用之前不会迭代。
假设我有这个代码:
foreach(int iteratorInt in myIEnumerable.OrderBy(x => x))
{
if(iteratorInt == myIEnumerable.First())
{
// do something
}
}
在 if
中,我正在检查第一个元素,因此每次迭代都必须对 myIEnumerable
进行排序,以查看哪个是第一个元素,或者它只被排序一次?
对于此代码:
foreach(int iteratorInt in myIEnumerable.OrderBy(x => x.MyProperty))
OrderBy
只执行一次
您的 Enumerable 将只订购一次,此处:myIEnumerable.OrderBy(x => x)
在这一行 if(iteratorInt == myIEnumerable.First())
将不会再次订购。
可能你误解了IEnumerable.First
方法,IEnumerable.First
和IEnumerable.OrderBy
方法没有任何关系。
Console.WriteLine(new [] {3, 2, 1}.First());
// here you get 3, not 1.
你可以在这里看到自定义的OrderBy
方法:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var myIEnumerable = GetMyEnumerable();
foreach(var item in myIEnumerable.MyCustomOrderBy(x => x))
{
if(item == myIEnumerable.First())
{
Console.WriteLine("The condition is true with: " + item);
}
}
}
public static IEnumerable<int> GetMyEnumerable()
{
foreach(var i in new int[] {5, 4, 3, 2, 1})
{
Console.WriteLine("GetMyEnumerable was called " + i);
yield return i;
}
}
}
public static class OrderByExtensionMethod
{
public static IOrderedEnumerable<TSource> MyCustomOrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
Console.WriteLine("OrderByExtensionMethod was called");
return source.OrderBy(keySelector);
}
}
输出:
OrderByExtensionMethod was called
GetMyEnumerable was called 5
GetMyEnumerable was called 4
GetMyEnumerable was called 3
GetMyEnumerable was called 2
GetMyEnumerable was called 1
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
The condition is true with: 5
发生了什么事?
首先调用了MyCustomOrderBy
方法,他需要遍历整个集合,对元素进行排序。
OrderByExtensionMethod was called
GetMyEnumerable was called 5
GetMyEnumerable was called 4
GetMyEnumerable was called 3
GetMyEnumerable was called 2
GetMyEnumerable was called 1
然后你的foreach开始,myIEnumerable.First()
对每一项执行:
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
GetMyEnumerable was called 5
终于如愿以偿了:
The condition is true with: 5
OrderBy
只计算一次,然而,每次迭代都会在原来无序myIEnumerable
的基础上创建一个新的IEnumerator<T>
,它可能不会匹配iteratorInt
在第一次迭代时,除非第一个元素恰好是将被排序到第一个位置的元素。
如果您做希望 iteratorInt
的第一次迭代值与可枚举的 First()
结果相匹配。您可能希望在循环 之前创建有序可枚举 的临时副本,如下所示:
var list = myIEnumerable.OrderBy(x => x);
foreach(int iteratorInt in list)
{
if(iteratorInt == list.First())
{
// do something
}
}
尽管这是一个毫无意义的模式(类似于“Loop-switch”反模式),因为它可以简化为:
//do something with list.First();
使用 LINQ 扩展时,查询只会在请求时执行,否则称为延迟执行。当多次请求同一个查询时,每次都会重新评估基础查询,除非初始查询已通过 .ToArrary()
或 .ToList()
.
问题还不是很清楚,所以我将提供一些示例来演示各种行为。
示例 1:
- 将初始请求设置为局部变量。
- 在 foreach 中应用 LINQ 查询来订购集合。
- 使用初始局部变量查找第一个结果。
- 不要实现任何结果。
代码:
private static void Ex1()
{
Console.WriteLine("A");
IEnumerable<int> myIEnumerable = GetEnumerable();
Console.WriteLine("B");
foreach (int i in myIEnumerable.OrderBy(x => x))
{
Console.WriteLine("*** foreach : " + i);
if (i == myIEnumerable.First())
{
Console.WriteLine("=== Matched .First() : " + i);
}
}
Console.WriteLine("C");
}
示例 2:
- 将初始请求设置为局部变量。
- 在 foreach 外部应用 LINQ 查询以在不具体化结果的情况下对集合进行排序。
- 使用有序查询查找第一个结果
- 不要实现任何结果。
代码:
private static void Ex2()
{
Console.WriteLine("A");
IEnumerable<int> myIEnumerable = GetEnumerable();
Console.WriteLine("B");
var ordered = myIEnumerable.OrderBy(x => x);
foreach (int i in ordered)
{
Console.WriteLine("*** foreach : " + i);
if (i == ordered.First())
{
Console.WriteLine("=== Matched .First() : " + i);
}
}
Console.WriteLine("C");
}
示例 3:
- 将初始请求设置为局部变量。
- 在 foreach 外部应用 LINQ 查询来排序集合并具体化结果。
- 使用有序查询查找第一个结果。
代码:
private static void Ex3()
{
Console.WriteLine("A");
IEnumerable<int> myIEnumerable = GetEnumerable();
Console.WriteLine("B");
var ordered = myIEnumerable.OrderBy(x => x).ToArray();
foreach (int i in ordered)
{
Console.WriteLine("*** foreach : " + i);
if (i == ordered.First())
{
Console.WriteLine("=== Matched .First() : " + i);
}
}
Console.WriteLine("C");
}
所有查询都使用相同的方法来获取可枚举:
private static IEnumerable<int> GetEnumerable()
{
Console.WriteLine("~~~ GetEnumerable Start");
foreach (int i in new[]{3, 2, 1})
{
Console.WriteLine(">>> yield return : " + i);
yield return i;
}
Console.WriteLine("~~~ GetEnumerable End");
}
最终结果为:
====================
Ex A
====================
A
B
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
*** foreach : 1
~~~ GetEnumerable Start
>>> yield return : 3
*** foreach : 2
~~~ GetEnumerable Start
>>> yield return : 3
*** foreach : 3
~~~ GetEnumerable Start
>>> yield return : 3
=== Matched .First() : 3
C
====================
Ex B
====================
A
B
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
*** foreach : 1
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
=== Matched .First() : 1
*** foreach : 2
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
*** foreach : 3
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
C
====================
Ex C
====================
A
B
~~~ GetEnumerable Start
>>> yield return : 3
>>> yield return : 2
>>> yield return : 1
~~~ GetEnumerable End
*** foreach : 1
=== Matched .First() : 1
*** foreach : 2
*** foreach : 3
C