【问题标题】:Avoiding default constructors and public property setters避免默认构造函数和公共属性设置器
【发布时间】:2016-02-06 19:11:18
【问题描述】:

我正在使用 SignalR 开展一个项目,并且我有一些我将通过它传递的对象。这些对象仅在我的后端代码中显式创建,我真的希望能够对它们强制执行不变性和不变量。我遇到了 SignalR 需要我(嗯,真的是 NewtonSoft.Json)在我的属性上有一个默认的、无参数的构造函数和公共设置器的问题,以便它通过网络对它们进行序列化和反序列化。

这是一个人为的例子:

public class Message{
    public string Text {get;set;}
    public int Awesomeness {get;set;}
}

我想要的是更多类似这些方面的东西(它可能应该具有只读私有字段和仅 getter 属性以完全不可变,但对于只是一个没有方法的 POCO 来说,已经足够好了)

public class Message {
    public string Text {get;private set;}
    public int Awesomeness {get;private set;}
    public Message( string msg, int awesome){
        if (awesome < 1 || awesome > 5){
            throw new ArgumentOutOfRangeException("awesome");
        }
        Text = msg;
        Awesomeness = awesome;
    }
}

但是,如果我这样做,我的对象将无法被 SignalR .NET 客户端库反序列化。我可以在其中放置一个默认构造函数,并将我的设置器公开,但是我必须记住不要在我的代码中使用它们,并确保团队中的其他人在不理解的情况下使用它们。

我已经提出了这样做的想法,将默认构造函数标记为永远不应该显式使用的东西:

[Obsolete("Bad! don't do this!")
public Message(){}

但我不能只在属性的 setter 上使用 Obsolete 属性。

如果我真的想这样做,我可以将“真实”对象从 DTO 表示中分离出来并在它们之间进行转换,但我并不想写一堆样板来做到这一点并引入另一层。

有什么我忽略了,还是我只需要硬着头皮处理它?

【问题讨论】:

  • 不确定这是否可行,但如果您将参数设置为可选,反序列化器是否工作?
  • 您是否尝试过使用[JsonProperty] 和[JsonConstructor] 属性?您可以将它们放在私人成员上,以让 Json.Net “看到”它们。您还可以为您的对象创建 JsonConverters。见JSON.net: how to deserialize without using the default constructor?。
  • 您确定 setters 需要公开吗?我以为只要有私人二传手在场,你就可以逍遥法外?

标签: c# json.net signalr


【解决方案1】:

如果您的类没有公共无参数构造函数,但确实有一个带参数的公共构造函数,Json.NET 将调用该构造函数,使用反射按名称将构造函数参数与 JSON 属性匹配 并为缺失的属性使用默认值。按名称匹配是不区分大小写的,除非有多个仅大小写不同的匹配项,在这种情况下,匹配项区分大小写。因此,如果您只是这样做:

public class Message
{
    public string Text { get; private set; }
    public int Awesomeness { get; private set; }
    public Message(string text, int awesomeness)
    {
        if (awesomeness < 1 || awesomeness > 5)
        {
            throw new ArgumentOutOfRangeException("awesome");
        }
        this.Text = text;
        this.Awesomeness = awesomeness;
    }
}

您将能够使用 Json.NET 成功地序列化和反序列化您的类。

原型fiddle.

如果你的类有多个公共构造函数,都带有参数,你可以用[JsonConstructor]标记一个使用,例如:

public class Message
{
    public string Text { get; private set; }
    public int Awesomeness { get; private set; }

    public Message(string Text)
        : this(Text, 1)
    {
    }

    [JsonConstructor]
    public Message(string text, int awesomeness)
    {
        if (awesomeness < 1 || awesomeness > 5)
        {
            throw new ArgumentOutOfRangeException("awesome");
        }
        this.Text = text;
        this.Awesomeness = awesomeness;
    }
}

另见JsonSerializerSettings.ConstructorHandling,它告诉 Json.NET 是否更喜欢非公共无参数构造函数而不是单个公共构造函数有参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 2021-05-21
    • 2022-01-08
    • 1970-01-01
    相关资源
    最近更新 更多