避免使用默认构造函数和公共属性setter

Eri*_*rds 2 c# json.net signalr

我正在研究SignalR的一个项目,我有一些我将要通过它的对象.这些对象只在我的后端代码中显式创建,我真的希望能够对它们强制实现不变性和不变量.我遇到了SignalR要求我(好吧,实际上是NewtonSoft.Json)的问题,在我的属性上有一个默认的,无参数的构造函数和公共setter,以便它通过网络序列化和反序列化它们.

这是一个人为的例子:

public class Message{
    public string Text {get;set;}
    public int Awesomeness {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

我想要的是,更多的是这些内容(它应该只有readonly私有字段和getter-only属性完全不可变,但对于只有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;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我这样做,我的对象不能被SignalR .NET客户端库反序列化.我可以在那里查找一个默认构造函数,然后公开我的setter,但是我必须记住不要在我的代码中使用它们,并确保团队中没有其他人在不理解的情况下使用它们.

我已经开始尝试这样做,将默认构造函数标记为永远不应该显式使用的东西:

[Obsolete("Bad! don't do this!")
public Message(){}
Run Code Online (Sandbox Code Playgroud)

但是我不能仅仅在属性的setter上使用Obsolete属性.

如果我真的想要,我可以从DTO表示中分离出"真实"对象并在它们之间进行转换,但是我真的很兴奋写一堆样板来做这件事并引入另一层.

有什么我忽略的,或者我只需要咬紧牙关并处理它?

dbc*_*dbc 5

如果您的类没有公共无参数构造函数,但确实有一个带参数的公共构造函数,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

原型小提琴.

如果您的类有多个公共构造函数,都带有参数,您可以标记要使用的构造函数[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;
    }
}
Run Code Online (Sandbox Code Playgroud)

另请参阅JsonSerializerSettings.ConstructorHandling哪个告诉Json.NET是否优先使用带有参数的单个公共构造函数的非公共无参数构造函数.