C# 当没有相应的值进行反序列化时,如何使用构造函数的默认参数值?

C# 当没有相应的值进行反序列化时,如何使用构造函数的默认参数值?,c#,json.net,C#,Json.net,我有一个类,它直接在代码中实例化,也由反序列化程序实例化。通常,ImmutablePropertyStore对象的JSON表示形式中存在的所有构造函数参数都有一个值——在本例中,一切正常。但是,有时JSON表示中可能缺少一些或所有值。在这种情况下,我希望json.NET使用构造函数中提供的默认参数。但是,它似乎忽略了这些默认参数,而是实例化了该类,并将缺少的值设置为该数据类型的默认值—如果是布尔值,则为false 下面的示例代码说明了这个问题(实际上,还有很多属性): 使用系统; 使用Newto

我有一个类,它直接在代码中实例化,也由反序列化程序实例化。通常,ImmutablePropertyStore对象的JSON表示形式中存在的所有构造函数参数都有一个值——在本例中,一切正常。但是,有时JSON表示中可能缺少一些或所有值。在这种情况下,我希望json.NET使用构造函数中提供的默认参数。但是,它似乎忽略了这些默认参数,而是实例化了该类,并将缺少的值设置为该数据类型的默认值—如果是布尔值,则为false

下面的示例代码说明了这个问题(实际上,还有很多属性):

使用系统;
使用Newtonsoft.Json;
公共课程
{
公共静态void Main()
{
ImmutablePropertyStore ImmutablePropertyStore=新的ImmutablePropertyStore();
Console.WriteLine(immutablePropertyStore);
字符串jsonRepresentation=JsonConvert.SerializeObject(immutablePropertyStore,Formatting.Indented);
WriteLine(JsonConvert.DeserializeObject(jsonRepresentation));
//所需输出:属性A为“真”,属性B为“真”。实际输出:属性A为“假”,属性B为“真”。
WriteLine(JsonConvert.DeserializeObject(“{\'PropertyB\':true}”);
//所需输出:属性A为“真”,属性B为“假”。实际输出:属性A为“假”,属性B为“假”。
WriteLine(JsonConvert.DeserializeObject(“{}”);
}
}
公共类ImmutablePropertyStore
{
公共财产{get;}
公共布尔属性b{get;}
公共ImmutablePropertyStore(boolPropertyA=true,boolPropertyB=false)
{
PropertyA=PropertyA;
PropertyB=PropertyB;
}
公共重写字符串ToString()
{
return$“属性A是{PropertyA}”,属性B是{PropertyB}”;
}
}
或者作为.NET小提琴手:

我希望输出的最后一行是:

属性A为“真”,属性B为“假”

而不是:

属性A为“假”,属性B为“假”

我如何通过以下方式实现这一点:

  • 保持类的不变性(无公共设置器)和

  • 允许在一个位置指定默认值,并用于直接实例化和通过反序列化进行实例化


  • 您可以编写一个自定义的
    JsonConverter
    ,或者将
    ImmutablePropertyStore
    的构造函数参数设置为null,并将其默认值设置为
    null
    。然后在ctor中,您可以确定“真实”默认值应该是什么

    public ImmutablePropertyStore(bool? propertyA = null, bool? propertyB = null)
    {
      PropertyA = propertyA ?? true;
      PropertyB = propertyB ?? false;
    }
    
    正如您所提到的,将使用属性类型的默认值(
    null
    用于可为空的类型),然后将其转换为ctor中正确的“实”值


    使用此方法时,默认值不太明显,但我不确定这是否会成为问题。

    我不知道您的完整用例,这是否与您正在寻找的一样?将
    DefaultValue
    属性与
    JsonSerializerSettings
    结合使用

    使用系统;
    使用系统组件模型;
    使用Newtonsoft.Json;
    公共课程
    {
    公共静态void Main()
    {
    JsonConvert.DefaultSettings=()=>新的JsonSerializerSettings
    {
    DefaultValueHandling=DefaultValueHandling.Populate
    };
    WriteLine(JsonConvert.DeserializeObject(“{}”);
    WriteLine(JsonConvert.DeserializeObject(@“{propertyB:“true”}”);
    WriteLine(JsonConvert.DeserializeObject(@“{PropertyA:“false”,PropertyB:“true”}”);
    }
    }
    公共类ImmutablePropertyStore
    {
    [默认值(真)]
    公共财产{get;}
    [默认值(false)]
    公共布尔属性b{get;}
    公共ImmutablePropertyStore(bool propertyA、bool propertyB)
    {
    PropertyA=PropertyA;
    PropertyB=PropertyB;
    }
    公共重写字符串ToString()
    {
    return$“属性A是{PropertyA}”,属性B是{PropertyB}”;
    }
    }
    
    输出:

    Property A is 'True' and Property B is 'False'.
    Property A is 'True' and Property B is 'True'.
    Property A is 'False' and Property B is 'True'.
    
    我看到这些问题,

    • 在这方面,使类不可变在某种程度上是一种编码练习,因为类不是密封的,如果使用者想要获取对象并对其进行更改,那么这是可以做到的
    • 现在只有两个默认值,但如果有更多的默认值呢?这一切的复杂性是指数增长的

    回答

    我建议您更改设计,使一个基类的所有属性都是可变的,这将是任何反序列化操作的目标(因为可变属性可以很好地进行反序列化)。然后使用者将通过covert/copy/reflect从该基类获取不可变实例

    var bse = JsonConvert.DeserializeObject<MutablePropertyStore>("{ 'PropertyB' : true }");
    Console.WriteLine("Base:    " +  bse.ToString());   
    
    var derived = new ImmutablePropertyStore(bse);              
    Console.WriteLine("Derived: " + derived.ToString());    
    

    范例

    代码

    public sealed class ImmutablePropertyStore : MutablePropertyStore
    {
        public new bool PropertyA { get; private set; }
        public new bool PropertyB { get; private set; }
    
        public ImmutablePropertyStore() { }
    
        public ImmutablePropertyStore(MutablePropertyStore ms)
        {
            PropertyA = ms.PropertyA;
            PropertyB = ms.PropertyB;
        }
    
        public ImmutablePropertyStore(bool propertyA = true, bool propertyB = false)
        {
            PropertyA = propertyA;
            PropertyB = propertyB;
        }
    
        public override string ToString() 
            => $"Property A is '{PropertyA}' and Property B is '{PropertyB}'."; 
    }
    
    public class MutablePropertyStore
    {
        public virtual bool PropertyA { get; set;}
        public virtual bool PropertyB { get; set;}
    
        // Set all defaults here
        public MutablePropertyStore() { PropertyA = true; }
    
        public override string ToString() 
            => $"Property A is '{PropertyA}' and Property B is '{PropertyB}'.";      
    
    }
    

    这似乎是在缺少某些值时唯一有效的答案,但并不完全感谢您的建议。不幸的是,这将阻止使用任何缺少的参数直接实例化ImmutablePropertyStore(“例如,新ImmutablePropertyStore()”将不再编译)。在我的用例中,我需要将默认值用于直接实例化和通过反序列化进行实例化。我认为这个解决方案对于我的特殊情况来说可能是过分了——不过,密封类可能是个好主意。此外,我建议复杂性(取决于它的定义)保持不变,并且随着添加更多属性,类的大小将线性增长。没有什么东西会成倍增长。
    Base:    Property A is 'True' and Property B is 'True'.
    Derived: Property A is 'True' and Property B is 'True'.
    
    public sealed class ImmutablePropertyStore : MutablePropertyStore
    {
        public new bool PropertyA { get; private set; }
        public new bool PropertyB { get; private set; }
    
        public ImmutablePropertyStore() { }
    
        public ImmutablePropertyStore(MutablePropertyStore ms)
        {
            PropertyA = ms.PropertyA;
            PropertyB = ms.PropertyB;
        }
    
        public ImmutablePropertyStore(bool propertyA = true, bool propertyB = false)
        {
            PropertyA = propertyA;
            PropertyB = propertyB;
        }
    
        public override string ToString() 
            => $"Property A is '{PropertyA}' and Property B is '{PropertyB}'."; 
    }
    
    public class MutablePropertyStore
    {
        public virtual bool PropertyA { get; set;}
        public virtual bool PropertyB { get; set;}
    
        // Set all defaults here
        public MutablePropertyStore() { PropertyA = true; }
    
        public override string ToString() 
            => $"Property A is '{PropertyA}' and Property B is '{PropertyB}'.";      
    
    }