Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prove the method which returns IEnumerable has been called twice?

Tags:

c#

In Visual Studio, ReSharper warns: "Possible multiple enumeration of IEnumerable" for the following code:

static void Main(string[] args)
{
    IEnumerable<string> items = Test2();
    foreach (var item in items)
    {
        Console.WriteLine(item);
    }
    var newitems = new StringBuilder();
    foreach (var item in items)
    {
        newitems.Append(item);
    }
}

private static IEnumerable<string> Test2()
{
    string[] array1 = { "1", "2", "3" };
    return array1;
}

I expect that the Test2 method will be called twice, but it's called once.

What am I missing?

like image 512
tesicg Avatar asked Oct 25 '25 02:10

tesicg


2 Answers

It's only called once because Test2() actually returns string [] which is also an IEnumerable<string>. This string [] array remains referenced by items so each time you use items you just re-use the array.

The case you're expecting is an implementation of Test2() with an iterator block :

private static IEnumerable<string> Test2()
{
    string[] array1 = { "1", "2", "3" };

    foreach (var str in array1) 
    {
        yield return str; 
    }
}
like image 113
Perfect28 Avatar answered Oct 26 '25 15:10

Perfect28


Take a look at this example:

void Main()
{
    IEnumerable<int> items = Test2();
    foreach (var item in items)
    {
        Console.WriteLine(item);
    }
    var newitems = new StringBuilder();
    foreach (var item in items)
    {
        newitems.Append(item);
    }
}

IEnumerable<int> Test2()
{
    Console.WriteLine("Test2 called");
    return GetEnum();
}

IEnumerable<int> GetEnum()
{
    for(var i = 0; i < 5; i ++)
    {
        Console.WriteLine("Doing work...");
        Thread.Sleep(50); //Download some information from a website, or from a database
        yield return i;
    }
}

Imagine that return GetEnum(); was return new int[] { 1, 2, 3 }

Now, with arrays, iterating them multiple times isn't necessarily a bad thing. In your case, you can do the work in one loop, but that's not the reason resharper warns you. It warns you because of the possibility that Test2() returns a lazy enumerable that does work every time it's iterated.

If you run the above code, you'll get this output:

Test2 called
Doing work...
0
Doing work...
1
Doing work...
2
Doing work...
3
Doing work...
4
Doing work...
Doing work...
Doing work...
Doing work...
Doing work...

Note that Test2 itself is only called once, but the enumerable is iterated twice (and the work is done twice!).

You can avoid this by writing:

var items = Test2().ToList();

Which will immediately evaluate the enumerable and put it into a list. In this case, the work is only done once.

like image 25
Rob Avatar answered Oct 26 '25 15:10

Rob