为什么不能在 IEnumerable 循环中修改 属性 bag (Dictionary<string, object>)

Why can't property bag (Dictionary<string, object>) be modified inside IEnumerable loop

考虑以下测试程序,其中我(ab)使用字典来包含可能具有未知字段(以及这些字段的未知类型)的文档,

Fiddle of the program

using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;

public class Program
{
    public static void Main()
    {
        var docs = GetDocuments();
        foreach(var doc in docs){
            doc["a"] = new string[]{"Hello", "World!"};
            var docInLoop = JsonConvert.SerializeObject(doc);
            Console.WriteLine(docInLoop);
        }

        var serialized = JsonConvert.SerializeObject(docs);
        Console.WriteLine("===========================================================================================");
        Console.WriteLine(serialized);
        Console.WriteLine("===========================================================================================");
        var bar = docs.First()["a"] as string[];        
        Console.Write("First entry of first document is string[]?");
        Console.WriteLine(bar==null? " No" : "Yes");


    }

    public static IEnumerable<Document> GetDocuments(){
        return Enumerable.Range(0, 10).Select(i => {

        var doc = new Document();
        doc["a"] = new int[]{1,2,3,4,5,6};
        return doc;
        });
    }

    public class Document : Dictionary<string, object>{}
}

当 运行 时,预期是因为在 foreach 循环中我修改了文档,文档集合应该被修改。但这是输出:

{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
{"a":["Hello","World!"]}
===========================================================================================
[{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]},{"a":[1,2,3,4,5,6]}]
===========================================================================================
First entry of first document is string[]? No

从集合的反序列化来看,在循环中改变文档没有效果?这怎么可能?我错过了什么?我在循环中直接引用了文档对象...

I have a direct reference to the document object in the loop...

不,你不知道。您可以参考如何制作这些数据的方法! 这就是延迟执行的意义所在。

实际上第一次在这一行执行查询:

foreach(var doc in docs){

这是你第一次做饭。你用自己的配料给它调味得很好。

当您在这一行中序列化 doc 时:

var serialized = JsonConvert.SerializeObject(docs);

您基本上是在再次执行 GetDocuments 中的查询。这与您写的一样:

var serialized = JsonConvert.SerializeObject(GetDocuments());

这基本上意味着你要重新做饭。按照食谱做,不过这次和上次一样不加任何材料。然后你想知道为什么汤尝起来不像你第一次放的香料。

如果您在循环之前使用 ToList() 调用具体化结果,您将获得所需的结果:

var docs = GetDocuments().ToList();

这是一个不错的article elaborating on the traps of deferred execution