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

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

这是一个人为的例子:

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

我想要的是,更多的是这些内容(它应该只有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  5){ throw new ArgumentOutOfRangeException("awesome"); } Text = msg; Awesomeness = awesome; } } 

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

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

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

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

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

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

如果您的类没有公共无参数构造函数,但确实有一个带参数的公共构造函数,Json.NET将调用该构造函数, 使用reflection按名称将构造函数参数与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成功序列化和反序列化您的类。

原型小提琴 。

如果你的类有多个公共构造函数,都有参数,你可以用[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是否JsonSerializerSettings.ConstructorHandling使用带有参数的单个公共构造函数的非公共无参数构造函数。