JObject正在丢失引用



我有两个闪烁显示问题。

场景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。如果Parentnull,则将未修改地分配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

可以看出,在偶数次分配之后,arrParent被设置为null

在第一个分配之后,arr.Parent被设置(JProperty包含Name及其属于JOjectValue(。在第二次分配期间,由于arr已经具有Parent,因此将克隆arr,并且克隆的值将设置为obj["arr"]。当设置新的JToken时,先前JTokenParent将被设置为null,即arr.Parent将变为null

再次在第三次分配中,arr.Parentnull。因此,它将像第一个作业一样设置。

对于场景2,arr已经具有Parent,因此在第二次分配期间,即obj2["arr"] = arr;arr将被克隆和设置。

相关内容

  • 没有找到相关文章

最新更新