简体   繁体   中英

Foreach and Linq Statement Equalization

According to Resharper this two shall do the same:

1)

string strTemp = null;
foreach (var s in array)
{
    if (strTemp == null || !strTemp.Contains(s))
    {
        strTemp += (!string.IsNullOrEmpty(strTemp) ? ";" : string.Empty) + s;
    }
}

2)

string strTemp = null;
foreach (var s in array.Where(s => strTemp == null || !strTemp.Contains(s)))
{
    strTemp += (!string.IsNullOrEmpty(strTemp) ? ";" : string.Empty) + s;
}

How so? What am I missing?

Judging by comments, you're expecting that this lambda expression will only be executed once, while strTemp still has a value of null .

s => strTemp == null || !strTemp.Contains(s)

That's not the case. It will execute for each element of the array, and will use the latest value of strTemp in each case. Even though the Where method is only called once, the lambda expression (or rather, the delegate created from it) will be executed for each element in array .

Your code would probably be better written as:

string joined = string.Join(";", array.Distinct());

... however. In particular, in your current code, if your array is { "ab", "a", "ab", "c" } you'll end up with an output of just ab;c where I suspect you want ab;a;c ... this is because "a" is already contained in "ab" by the second iteration of the loop. Of course if you really want that behaviour, the code you've got will work... but it does sound unusual to me.

EDIT: To understand all this, you need to understand how LINQ uses lazy evaluation. The Where method returns immediately without having executed the predicate at all ... but when you ask the collection for its first element (via GetEnumerator() / MoveNext() ) it will iterate over the array, testing the predicate against each item until it finds one which matches. It will then yield that item, and when you ask it for the next item, it will carry on from where it had got to, again testing the predicate against the remaining elements of the array.

It may be easiest to demonstrate this with a little code:

using System;
using System.Linq;

class Test
{
    static void Main()
    {
        var numbers = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        var filtered = numbers.Where(x => {
            Console.WriteLine("Testing {0}", x);
            return x % 2 == 0;
        });
        Console.WriteLine("Just before loop");
        foreach (var item in filtered)
        {
            Console.WriteLine("Received {0}", item);
        }
    }
}

Output:

Just before loop
Testing 0
Received 0
Testing 1
Testing 2
Received 2
Testing 3
Testing 4
Received 4
Testing 5
Testing 6
Received 6
Testing 7
Testing 8
Received 8
Testing 9

As you can see:

  • Nothing is printed from the predicate until we start iterating
  • The output interleaves the testing with the handling of each included item

版本2个refactors的if -condition从版本1到的过滤器Where在函数调用foreach语句。

The if-block and the .Where method fullfill the same function.

In option 1 you iterate over the complete array and check if the variable matches the if statement.

In option 2 you apply a filter to the array which is equivalent to the if condition in option 1.

Then strTemp gets processed. The filtered data is identical in both cases, because the if-statement and the where clause are identical.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM