避免默认构造函数和公共属性设置器

时间:2016-02-06 19:11:18

标签: c# json.net signalr

我正在与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 < 1 || awesome > 5){
            throw new ArgumentOutOfRangeException("awesome");
        }
        Text = msg;
        Awesomeness = awesome;
    }
}

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

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

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

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

如果我真的想,我可以分离出真实的&#34;来自DTO表示的对象并在它们之间进行转换,但我并没有真正兴奋地编写一堆样板来执行此操作并引入另一层。

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

1 个答案:

答案 0 :(得分:5)

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

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是否优先选择非公共无参数构造函数而不是带有参数的单个公共构造函数。