使用 DataContract 实现不同类型(bool? 和 enum)之间的映射

Implement a mapping between different types (bool? and enum) using DataContract

我在用户机器上使用 DataContract 到 serialize/deserialize 对象。所以基本上是 save/load 场景。

我有一个 DataContract,它有一个 bool? 类型的有序 DataMember,现在我需要将它更改为 Enum。它是有序的,因为其中一个序列化程序是 protobuf-net.

现在的样子:

[DataContract]
public class Result
{ 
    [DataMember(Order = 3)]
    public bool? Accepted { get; set; }
}

需要是:

public enum ResultDecisionStatus
{
    Accepted, //// map as 'true' for 'bool?'
    Rejected, //// map as 'false' for 'bool?'
    Neutral, //// new
    Unknown //// map as 'null' for 'bool?'
}

[DataContract]
public class Result
{
    [DataMember(Order = 4?)]   //// I assume ordering might have to change
    public ResultDecisionStatus DecisionStatus { get; set; }
}

我实施了以下在我看来 hacky 但似乎有效的方法。我将 Accepted 保持在相同的顺序并将其更改为 private 并设置一个变量以在反序列化时将其映射到新的 Enum 。这是好的设计吗?感觉怪怪的。

[DataContract]
public class Result
{
    [DataMember(Order = 4)]
    public ResultDecisionStatus DecisionStatus { get; set; }

    [DataMember(Order = 3)]
    private bool? Accepted { get; set; }

    [DataMember(Order = 1003)]
    private bool AcceptedToDecisionStatusMapped { get; set; }

    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
        if (!AcceptedToDecisionStatusMapped)
        {
            switch(Accepted) 
            {
                case true:
                    DecisionStatus = ResultDecisionStatus.Accepted;
                    break;
                case false:
                    DecisionStatus = ResultDecisionStatus.Rejected;
                    break;
                case null:
                    DecisionStatus = ResultDecisionStatus.Unknown;
                    break;
            }
            AcceptedToDecisionStatusMapped = true;
        }
    }
}

问题:有没有什么正确的方法可以在不保留旧成员并为每个版本添加额外映射属性的情况下实现有序DataContracts类型之间的映射?

试试这个:

[DataContract]
public class Result
{ 
    private ResultDecisionStatus? _decisionStatus;

    [DataMember(Order = 3)]
    public bool? Accepted { get; set; }

    [DataMember(Order = 4)]
    public ResultDecisionStatus DecisionStatus 
    { 
        get
        {
            if (_decisionStatus.HasValue)
            {
                return _decisionStatus.Value;
            }
            else if (Accepted.HasValue)
            {
                return Accepted.Value 
                    ? ResultDecisionStatus.Accepted 
                    : ResultDecisionStatus.Rejected;
            }
            else
            {
                return ResultDecisionStatus.Unknown;
            }
        }
        set
        {
            _decisionStatus = value;
        }
    }
}

这应该基本上允许使用旧合同和新合同的数据进行互操作。如果同时使用 34 会变得很奇怪,但我认为在您的情况下它会是其中之一。

更新:

看我的另一个回答。在您的情况下,由于布尔值可以轻松映射到枚举值,因此有一种更简单的方法。对于可能没有这种奢侈的人,留下这个答案。

看起来 boolint32 在 protobuf 中是兼容的,所以你可以这样做:

public enum ResultDecisionStatus
{
    Rejected = 0, // will catch old boolean false values
    Accepted = 1, // will catch old boolean true values
    Neutral = 2 // new
}

[DataContract]
public class Result
{ 
    [DataMember(Order = 3)]
    public ResultDecisionStatus? DecisionStatus { get; set; } 
}

就用null代表Unknown