防止setter在使用Newtonsoft JSON.NET进行反序列化时重新运行操作

EJo*_*ica 1 .net c# json json.net deserialization

所以我创建了一个Monopoly控制台应用程序来探索一些设计概念,以及尝试更好地理解测试驱动开发.

在这种情况下,我创建了单元测试,以确保我的所有类都可以正确地序列化和反序列化(因为我将来需要它,如果他们不能,我宁愿早点知道,所以我可以避免返工;事实上,这样做有助于我在初始设计中遇到一些缺陷.

此属性是较大的Property类的一部分.Owner是拥有该物业的玩家.

我在这里想要实现的是,当一个玩家抵押某个特定的房产时,该房产应该被标记为"抵押",玩家应该以现金收取购买价格的一半.当他们不归还财产时,他们会还钱.还有一些我还没有写过的其他检查; 例如,游戏规则规定,如果房产仍有房屋,则不允许抵押房产.我还没有检查玩家是否有足够的资金来取消房产的抵押,但我计划尽快补充,因为这是一个微不足道的规则来实施.这就是为什么我在这里使用setter而不是自动生成的属性.

这是问题所在:这序列化和反序列化而不抛出异常,但是如果我序列化抵押财产,它会在我反序列化时再添加钱(即反序列化基本上具有重新抵押财产的效果),所以不必要说单元测试失败(它应该给出一个明显的错误).我认为这并不奇怪,因为这正是我写它的方式 - 当你改变它的价值时,它要么抵押要么不归还财产.

这是代码:

[JsonProperty(Order = 1)]
public Player Owner
{
    get;
    set;
}

    private bool _isMortaged = false;

    [JsonProperty(Order = 2)]
    public bool IsMortgaged
    {
        get { return _isMortaged; }
        set
        {
            if (!IsOwned)
            {
                throw new InvalidOperationException("You can't mortgage a property that no one owns");
            }

            // If we're setting this property to the same thing that it
            // already was, log it. You could argue that that would indicate
            // a bug (it probably would), but throwing an exception here
            // breaks deserialization.
            Debug.WriteIf(value == _isMortaged, $"Setting IsMortgaged to the same thing as it was before. Stack trace:{Environment.NewLine}{(new StackTrace()).ToString()}");

            // If we're setting this to the same thing that it already was,
            // then the setter should have no effect at all.
            if (value != _isMortaged)
            {
                // The player is mortgaging the property
                if (value)
                {
                    _isMortaged = true;
                    Owner.Money += (PurchasePrice / 2);
                }
                // The player is unmortgaging the property
                else
                {
                    _isMortaged = false;
                    Owner.Money -= (PurchasePrice / 2);
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

是否有一种简单的方法可以使用不同的setter进行反序列化,或者判断我是否在setter中进行反序列化?(后者似乎是"黑客").我想也有可能编写一个自定义反序列化器,它可以解决我想的问题,但我宁愿避免这种情况(如果可能的话)(这似乎不是一种特别"干净"的方式).

或者,这只是一个设计缺陷吗?

Cli*_*int 7

这是一个设计缺陷; setters不应该有明显的更新值(以及那些预期的那些INotifyPropertyChanged)的副作用.

如果你需要特殊的行为,那么要么引入一种特殊的设置方法,要么反过来考虑它:

您正在抵押/取消抵押房产,因此您希望该房产的余额的所有者进行更新

而不是让财产更新所有者的余额(它不应该关注:分离关注 ;您可以改为拥有所有者的余额属性:

public class Property {
    public bool IsMortgaged {get;set;}
}

public class Player
{
    private List<Property> _properties = new List<Property>();
    private double _cash = 0;

    public int AvailableMoney =>
        _cash + _properties.Where(p => p.IsMortgaged).Select(p => p.MortgageValue).Sum();
}
Run Code Online (Sandbox Code Playgroud)

现在,显然需要更新以正确匹配您的模型,但理想情况下您希望您的数据尽可能愚蠢 ; 财产是否抵押是一个简单的真/假.这是特殊的平衡,没有理由不重新计算它.