Debugging 为什么Newtonsoft Replace函数仅在值发生更改时才替换该值?

Debugging 为什么Newtonsoft Replace函数仅在值发生更改时才替换该值?,debugging,json.net,Debugging,Json.net,以下面的代码为例: JProperty toke = new JProperty("value", new JValue(50)); //toke.Value is 50 toke.Value.Replace(new JValue(20)); //toke.Value is 20 JValue val0 = new JValue(50); JProperty toke = new JProperty("value", val0); //toke.Value is 50 JValue val1

以下面的代码为例:

JProperty toke = new JProperty("value", new JValue(50)); //toke.Value is 50
toke.Value.Replace(new JValue(20)); //toke.Value is 20
JValue val0 = new JValue(50);
JProperty toke = new JProperty("value", val0); //toke.Value is 50
JValue val1 = new JValue(20);
toke.Value.Replace(val1); //toke.Value is 20
这正如预期的那样有效。现在检查以下代码:

JProperty toke = new JProperty("value", new JValue(50)); //toke.Value is 50
toke.Value.Replace(new JValue(20)); //toke.Value is 20
JValue val0 = new JValue(50);
JProperty toke = new JProperty("value", val0); //toke.Value is 50
JValue val1 = new JValue(20);
toke.Value.Replace(val1); //toke.Value is 20
这也如预期的那样起作用,但有一个重要的细节。val0不再是toke的JSON树的一部分,val1是JSON树的一部分;这意味着val0没有有效的父级,而val1有

现在用这个密码

JValue val0 = new JValue(50);
JProperty toke = new JProperty("value", val0); //toke.Value is 50
JValue val1 = new JValue(50);
toke.Value.Replace(val1); //toke.Value is 50
行为不同;val0仍然是托克JSON树的一部分,而val1则不是。现在,val0有一个有效的父级,而val1没有

这是一个关键的区别,如果您使用Newtonsoft JSON树来表示结构,并将JToken作为引用存储到树中,那么引用结构的方式可能会根据被替换的值而改变,这似乎是不正确的


我的推理有什么缺陷吗?或者行为是不正确的,正如我所认为的那样?

我认为您有一个正确的观点:
Replace
应该替换令牌实例并正确设置父项,即使令牌具有相同的值

如果属性值是一个
JObject
,并用相同的
JObject
替换它,则此操作与您预期的一样:

JObject obj1=JObject.Parse(@“{”foo”“:1}”);
JProperty prop=新的JProperty(“条形”,obj1);
JObject obj2=JObject.Parse(@“{”foo“:1}”);
属性值替换(obj2);
Console.WriteLine(“obj1父项为”+
(ReferenceEquals(obj1.Parent,prop)-“prop”:“not prop”);//“不是道具”
Console.WriteLine(“obj2父对象为”+
(ReferenceEquals(obj2.Parent,prop)-“prop”:“not prop”);//“道具”
然而,代码似乎是特意编写的,以便在
JValues
中以不同的方式工作。在这个过程中,我们看到了调用,而调用又反过来调用。在
JProperty
类中,
SetItem()
的实现方式如下:

internal override void SetItem(int索引,JToken项)
{
如果(索引!=0)
{
抛出新ArgumentOutOfRangeException();
}
如果(未更改(值、项目))
{
回来
}
如果(父项!=null)
{
((JObject)父项)。内部属性更改(this);
}
base.SetItem(0,item);
如果(父项!=null)
{
((作业对象)父项)。内部属性已更改(此项);
}
}
您可以看到,它检查值是否“未更改”,如果是,它将返回而不做任何操作。如果我们看一下该计划的实施情况,我们会发现:

内部静态布尔值保持不变(JToken currentValue,JToken newValue)
{
JValue v1=当前值作为JValue;
如果(v1!=null)
{
//null将转换为null类型的JValue
if(v1.Type==JTokenType.Null&&newValue==Null)
{
返回true;
}
返回v1.Equals(newValue);
}
返回false;
}
因此,如果当前令牌是
JValue
,它将检查它是否等于另一个令牌,否则该令牌将自动被视为已更改。而
Equals
对于
JValue
当然是基于底层原语本身是否相等


我无法解释这个实施决策背后的原因,但对作者来说,这似乎是值得的。我认为,“正确”的解决办法是让
SetItem
使用
ReferenceEquals(Value,item)
而不是
IsTokenUnchanged(Value,item)

谢谢您的回复。我已经提出了一个问题,我们将看看结果如何。就我个人而言,我认为这是正确的行为。
JValue
表示原始值。不幸的是,它们不能用值语义实现,必须用引用实现。当引用表示相同的值时,期望它们可以被替换应该是错误的。只有价值才重要,而不是持有价值的对象。