开发者

C# iterator is executed twice when composing two IEnumerable methods

I just started learning about C# iterator bu开发者_开发技巧t got confused with the flow of the program after reading the output of the program. The foreach with uniqueVals seems to be executed twice. My understanding is that the first few lines up to the line before "Nums in Square: 3" should not be there. Can anyone help to explain why this happens?

The output is:

Unique: 1
Adding to uniqueVals: 1
Unique: 2
Adding to uniqueVals: 2
Unique: 2
Unique: 3
Adding to uniqueVals: 3

Nums in Square: 3
Unique: 1
Adding to uniqueVals: 1
Square: 1
Number returned from Unique: 1
Unique: 2
Adding to uniqueVals: 2
Square: 2
Number returned from Unique: 4
Unique: 2
Unique: 3
Adding to uniqueVals: 3
Square: 3
Number returned from Unique: 9
static class Program
    {
      public static IEnumerable<T> Unique<T>(IEnumerable<T> sequence)
        {
            Dictionary<T, T> uniqueVals = new Dictionary<T, T>();

            foreach (T item in sequence)
            {
                Console.WriteLine("Unique: {0}", item);
                if (!uniqueVals.ContainsKey(item))
                {
                    Console.WriteLine("Adding to uniqueVals: {0}", item);
                    uniqueVals.Add(item, item);
                    yield return item;
                    Console.WriteLine("After Unique yield: {0}", item);
                }
            }
        }

        public static IEnumerable<int> Square(IEnumerable<int> nums)
        {
            Console.WriteLine("Nums in Square: {0}", nums.Count());
            foreach (int num in nums)
            {
                Console.WriteLine("Square: {0}", num);
                yield return num * num;
                Console.WriteLine("After Square yield: {0}", num);
            }
        }

        static void Main(string[] args)
        {
            var nums = new int[] { 1, 2, 2, 3 };
            foreach (int num in Square(Unique(nums)))
                Console.WriteLine("Number returned from Unique: {0}", num);
            Console.Read();
        }

    }


IEnumerables don't define any field for the size of the Enumerable, the extension method Count tries to do this for you by iterating through the whole collection and telling you how many items it finds.

The first iteration through Unique comes from this line:

Console.WriteLine("Nums in Square: {0}", nums.Count());

The second iteration comes from your foreach loop as expected.


I believe this line is causing your confusion:

Console.WriteLine("Nums in Square: {0}", nums.Count()); 

The IEnumerable<T> interface doesn't actually have a Count method. The method Count is what's known as an extension method. Essentially, it's a static method that operates on the publicly visible members of IEnumerable<T>. The way it works is to iterate over the entire collection and count the number of items as it goes.

That's why you enumerate it twice: once for the Count() method and once in your foreach loop.


Both the Count() and the foreach are causing the enumerater to execute your method

Here is a fix/hack for you

    public static IEnumerable<int> Square(IEnumerable<int> nums)
    {
        var list = nums.ToList();
        Console.WriteLine("Nums in Square: {0}", list.Count);
        foreach (int num in list)
        {
            Console.WriteLine("Square: {0}", num);
            yield return num * num;
            Console.WriteLine("After Square yield: {0}", num);
        }
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜