我有两个闪烁显示问题。
场景1
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var arr = new JArray();
arr.Add("apple");
var obj = new JObject();
obj["arr"] = arr;
obj["arr"] = arr;
arr.Add("mango");
foreach(var a in obj["arr"]){
Console.WriteLine(a);
}
}
}
这里obj["array"]
应该引用arr
,即更早地初始化。所以输出应该是
apple
mango
但是输出是
apple
场景2
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var arr = new JArray();
arr.Add("apple");
var obj = new JObject();
obj["arr"] = arr;
var obj2 = new JObject();
obj2["arr"] = arr;
arr.Add("mango");
foreach(var a in obj2["arr"]){
Console.WriteLine(a);
}
}
}
类似地,obj2["arr"]
应该引用arr
。但事实并非如此。所以预期的输出是
apple
mango
但是输出是
apple
我对csharp不是那么精通。如果我遗漏了什么,请告诉我。
编辑
添加了@Wyck在评论中提到的另一个场景。
场景3
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var arr = new JArray();
Console.WriteLine(arr.GetHashCode());
arr.Add("apple");
var obj = new JObject();
obj["arr"] = arr;
Console.WriteLine(obj["arr"].GetHashCode());
obj["arr"] = arr;
Console.WriteLine(obj["arr"].GetHashCode());
obj["arr"] = arr;
Console.WriteLine(obj["arr"].GetHashCode());
arr.Add("mango");
foreach(var a in obj["arr"]){
Console.WriteLine(a);
}
}
}
奇数次重复分配obj["arr"] = arr
可以返回arr
的原始引用,但偶数次则不能。
这将是的输出
10465620
10465620
1190878
10465620
apple
mango
请参阅更改了偶数分配的哈希代码。对于奇数赋值,它又变成了以前的样子。
如果你查看Newtonsoft.Json的源代码,你会发现当将数组分配给属性时,它会创建它的副本:
public JProperty(string name, object? content)
{
...
Value = IsMultiContent(content)
? new JArray(content)
: CreateFromContent(content);
}
JObject
的相关部分在这里。
通过获得obj2["arr"]
和arr
(均为JArray
类型(的哈希代码(.GetHashCode()
(,您可以在代码中轻松地测试这一点,并观察到它们会有所不同。
因此,为了能够添加到数组中,您需要在分配了属性后通过JObject
的实例访问它,或者您可以在添加元素时将数组重新分配给属性。
这个答案完全基于@dbc的评论。这种令人惊讶的行为是由于Json.net的实现方式造成的。原来的答案在这里。
所有JToken
都需要具有Parent
属性,并且只能具有一个Parent
。
obj["arr"] = arr;
这里,在设置arr
之前,验证arr
是否已经具有Parent
。如果Parent
是null
,则将未修改地分配arr
,否则将克隆arr
,并分配克隆的arr
。这就是情景1和情景2行为背后的原因。
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var arr = new JArray();
arr.Add("apple");
var obj = new JObject();
obj["arr"] = arr;
Console.WriteLine(arr.Parent == null ? "null" : arr.Parent.GetHashCode().ToString());
obj["arr"] = arr;
Console.WriteLine(arr.Parent == null ? "null" : arr.Parent.GetHashCode().ToString());
obj["arr"] = arr;
Console.WriteLine(arr.Parent == null ? "null" : arr.Parent.GetHashCode().ToString());
arr.Add("mango");
foreach(var a in obj["arr"]){
Console.WriteLine(a);
}
}
}
该代码块的输出将是
10566368
null
10566368
apple
mango
可以看出,在偶数次分配之后,arr
的Parent
被设置为null
。
在第一个分配之后,arr.Parent
被设置(JProperty
包含Name
及其属于JOject
的Value
(。在第二次分配期间,由于arr
已经具有Parent
,因此将克隆arr
,并且克隆的值将设置为obj["arr"]
。当设置新的JToken
时,先前JToken
的Parent
将被设置为null
,即arr.Parent
将变为null
。
再次在第三次分配中,arr.Parent
是null
。因此,它将像第一个作业一样设置。
对于场景2,arr
已经具有Parent
,因此在第二次分配期间,即obj2["arr"] = arr;
,arr
将被克隆和设置。