在这种情况下,IEnumerable订购了多少次?

时间:2017-10-20 10:09:24

标签: c# linq ienumerable

我知道IEnumerable<T>在调用之前不会迭代。

假设我有这段代码:

foreach(int iteratorInt in myIEnumerable.OrderBy(x => x))
{
    if(iteratorInt == myIEnumerable.First())
    {
        // do something
    }
}

if我正在检查第一个元素,所以每次迭代都必须对myIEnumerable进行排序,以查看哪个是第一个元素,或者它只被排序一次?

4 个答案:

答案 0 :(得分:1)

您的可枚举只会已订购一次,此处:myIEnumerable.OrderBy(x => x)
在此行if(iteratorInt == myIEnumerable.First()),它不会再次订购。

也许您误解了IEnumerable.First方法,IEnumerable.FirstIEnumerable.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

答案 1 :(得分:1)

使用LINQ扩展时,查询将仅在请求时执行,也称为延迟执行。当多次请求相同的查询时,每次都会重新评估基础查询,除非初始查询已使用string = string.replaceAll("(?U)[^-_/.,A-Za-zÀ-ÿ &&[^×÷]]+",""); .ToArrary()之类的内容实现。

这个问题并不完全清楚,所以我将提供一些展示各种行为的例子。

Ex 1:

  • 将初始请求设置为本地变量。
  • 在foreach中应用LINQ查询以订购集合。
  • 使用初始局部变量查找第一个结果。
  • 不要实现任何结果。

代码:

.ToList()

前2:

  • 将初始请求设置为本地变量。
  • 在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");
}

前3:

  • 将初始请求设置为本地变量。
  • 在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");
}

所有查询都使用相同的方法来获取可枚举的内容:

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");
}

答案 2 :(得分:0)

代码:

foreach(int iteratorInt in myIEnumerable.OrderBy(x => x.MyProperty))

OrderBy只执行一次

答案 3 :(得分:0)

OrderBy仅评估一次,但是,每次迭代都会创建一个基于原始无序IEnumerator<T>的新myIEnumerable,它可能与iteratorInt不匹配在第一次迭代中,除非第一个元素恰好是排序到第一个位置的东西。

如果您 希望第一次迭代的iteratorInt值与可枚举的First()结果相匹配。你想在循环之前创建有序可枚举的临时副本,如下所示:

var list = myIEnumerable.OrderBy(x => x);
foreach(int iteratorInt in list)
{
    if(iteratorInt == list.First())
    {
        // do something
    }
}

虽然这是一个相当无意义的模式(类似于&#34; Loop-switch&#34;反模式),因为它可以简化为:

//do something with list.First();