避免默认构造函数和公共属性设置器 [英] Avoiding default constructors and public property setters

查看:23
本文介绍了避免默认构造函数和公共属性设置器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working on a project with SignalR, and I've got some objects that I'm going to be passing along through it. These objects are only explicitly created in my back-end code, and I'd really like to be able to enforce immutability and invariants on them. I'm running into the issue that SignalR requires me (well, really NewtonSoft.Json), to have a default, no-args constructor and public setters on my properties in order for it to serialize and deserialize them over the wire.

这是一个人为的例子:

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

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

What I'd like, is something more along these lines (it should probably have readonly private fields and getter-only properties to be fully immutable, but for something that is just a POCO with no methods, good enough)

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 客户端库反序列化.我可以在其中放置一个默认构造函数,并将我的设置器公开,但是我必须记住不要在我的代码中使用它们,并确保团队中的其他人在不理解的情况下使用它们.

If I do that, though, my object can't get deserialized by the SignalR .NET client library. I can just chuck a default constructor in there, and make my setters public, but then I have to remember to not use them in my code, and make sure no one else on the team uses them without understanding.

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

I've kicked around the idea of doing this, to mark the default constructor as something that should never be explicitly used:

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

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

But I can't use the Obsolete attribute on just the setter of a property.

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

If I really wanted to, I could separate out the "real" object from a DTO representation and convert between them, but I'm not really psyched up to write a bunch of boilerplate to do that and introduce another layer.

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

Is there something I'm overlooking, or do I just need to bite the bullet and deal with it?

推荐答案

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

If your class does not have a public parameterless constructor, but does have a single public constructor with parameters, Json.NET will call that constructor, matching the constructor arguments to the JSON properties by name using reflection and using default values for missing properties. Matching by name is case-insensitive, unless there are multiple matches that differ only in case, in which case the match becomes case sensitive. Thus if you simply do:

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 成功地序列化和反序列化您的类.

You will be able to serialize and deserialize your class successfully with Json.NET.

原型小提琴.

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

If your class has multiple public constructors, all with parameters, you can mark the one to use with [JsonConstructor], e.g.:

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 是否更喜欢非公共无参数构造函数而不是带有参数的单个公共构造函数.

See also JsonSerializerSettings.ConstructorHandling which tells Json.NET whether to prefer a non-public parameterless constructor over a single public constructor with parameters.

这篇关于避免默认构造函数和公共属性设置器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆