Does “foreach” cause repeated Linq execution?

前端 未结 8 1706
自闭症患者
自闭症患者 2020-11-30 08:25

I\'ve been working for the first time with the Entity Framework in .NET, and have been writing LINQ queries in order to get information from my model. I would like to progr

8条回答
  •  庸人自扰
    2020-11-30 08:56

    It will execute the LINQ statement the same number of times no matter if you do .ToList() or not. I have an example here with colored output to the console:

    What happens in the code (see code at the bottom):

    • Create a list of 100 ints (0-99).
    • Create a LINQ statement that prints every int from the list followed by two * to the console in red color, and then return the int if it's an even number.
    • Do a foreach on the query, printing out every even number in green color.
    • Do a foreach on the query.ToList(), printing out every even number in green color.

    As you can see in the output below, the number of ints written to the console is the same, meaning the LINQ statement is executed the same number of times.

    The difference is in when the statement is executed. As you can see, when you do a foreach on the query (that you have not invoked .ToList() on), the list and the IEnumerable object, returned from the LINQ statement, are enumerated at the same time.

    When you cache the list first, they are enumerated separately, but still the same amount of times.

    The difference is very important to understand, because if the list is modified after you have defined your LINQ statement, the LINQ statement will operate on the modified list when it is executed (e.g. by .ToList()). BUT if you force execution of the LINQ statement (.ToList()) and then modify the list afterwards, the LINQ statement will NOT work on the modified list.

    Here's the output:

    Here's my code:

    // Main method:
    static void Main(string[] args)
    {
        IEnumerable ints = Enumerable.Range(0, 100);
    
        var query = ints.Where(x =>
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write($"{x}**, ");
            return x % 2 == 0;
        });
    
        DoForeach(query, "query");
        DoForeach(query, "query.ToList()");
    
        Console.ForegroundColor = ConsoleColor.White;
    }
    
    // DoForeach method:
    private static void DoForeach(IEnumerable collection, string collectionName)
    {
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine("\n--- {0} FOREACH BEGIN: ---", collectionName);
    
        if (collectionName.Contains("query.ToList()"))
            collection = collection.ToList();
    
        foreach (var item in collection)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write($"{item}, ");
        }
    
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine("\n--- {0} FOREACH END ---", collectionName);
    }
    

    Note about execution time: I did a few timing tests (not enough to post it here though) and I didn't find any consistency in either method being faster than the other (including the execution of .ToList() in the timing). On larger collections, caching the collection first and then iterating it seemed a bit faster, but there was no definitive conclusion from my test.

提交回复
热议问题