【问题标题】:Why can't property bag (Dictionary<string, object>) be modified inside IEnumerable loop [duplicate]为什么不能在 IEnumerable 循环中修改属性包(Dictionary<string, object>)[重复]
【发布时间】:2019-07-24 12:16:09
【问题描述】:

考虑以下测试程序,其中我(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

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

【问题讨论】:

  • Dictionary 不是不可变的,即使 IEnumerable 可能是。
  • 这在我看来可能是与IEnumerable 的延迟执行行为有关的问题。您是否尝试过创建文档的实际集合? (即类似var docs = GetDocuments().ToArray()
  • 我希望它能够修改字典,这就是我在循环中所做的。它没有,我应该能够将引用类型的任何实例分配给该字典中的任何键(包括重新分配给新的引用类型)。
  • @bassfader 好像你是对的......强制它评估修复它,但我再次希望 newtonsoft 评估 IEnumerable
  • but then again I would expect newtonsoft to evaluate the IEnumerable 它确实评估了可枚举。从而获得一组全新的Documents。注意 - 这与 newtonsoft 完全无关 - 如果您执行第二个 foreach 循环,您会看到完全相同

标签: c# ienumerable


【解决方案1】:

我在循环中直接引用了文档对象...

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

其实查询第一次是在这一行执行的:

foreach(var doc in docs){

这是你第一次在这儿煮饭。你用自己的配料很好地调味。

当你在这一行序列化doc时:

var serialized = JsonConvert.SerializeObject(docs);

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

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

这基本上意味着你再次做饭。按照食谱,但这次你没有像上次那样添加任何成分。然后你想知道为什么汤的味道不像你第一次放的香料。

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

var docs = GetDocuments().ToList();

这是一个很好的article 详细说明deferred execution 的陷阱

【讨论】:

  • 感谢您的精彩解释,我现在明白了。我只是不喜欢隐藏的实现,这让我想起了 Qt 中与浅拷贝的斗争:p
  • @arynaq 欢迎您。是的,浅拷贝也有很好的陷阱。但这里的情况更糟;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-01
  • 1970-01-01
  • 2012-03-30
  • 2013-04-04
相关资源
最近更新 更多