DataContractSerializer 和不可变类型.反序列化为已知对象实例

本文关键字:对象 实例 反序列化 不可变 类型 DataContractSerializer | 更新日期: 2023-09-27 18:30:48

我有一个类,它实际上是一个基于对象的枚举。类公开了一组静态对象,并且所有对象都使用这些相同的实例。例如(注意私有构造函数)

[DataContract]
public class FieldType
{
    public static readonly FieldType Default  = new FieldType(1, "Default");
    public static readonly FieldType Name     = new FieldType(2, "Name");
    public static readonly FieldType Etc      = new FieldType(3, "Etc");
    private FieldType(uint id, string name)
    {
        Id = id;
        Name = name;
    }
    [DataMember] public uint   Id   { get; private set; }
    [DataMember] public string Name { get; private set; }
    //snip other properties
}

这很好用,直到我必须跨 WCF 序列化。DataContractSerializer通过绕过构造函数来创建新对象。这将生成一个有效的 FieldType 对象,但它是一个不是我的静态实例之一的新实例。这使得与已知静态值的引用比较失败。

有没有办法覆盖类的序列化行为,以便我创建对象实例而不是填充提供给我的实例?

DataContractSerializer 和不可变类型.反序列化为已知对象实例

怀疑你可以做到:

[DataContract]
public class FieldType : IObjectReference
{
    object IObjectReference.GetRealObject(StreamingContext ctx)
        switch(Id) {
            case 1: return Default;
            case 2: return Name; // note this is a collision between static/non-static
            case 3: return Etc;
            default: throw new InvalidOperationException();
        }
    }
    public static readonly FieldType Default  = new FieldType(1, "Default");
    // note this is a collision between static/non-static
    public static readonly FieldType Name     = new FieldType(2, "Name");
    public static readonly FieldType Etc      = new FieldType(3, "Etc");
    private FieldType(uint id, string name)
    {
        Id = id;
        Name = name; // note this is a collision between static/non-static
    }
    [DataMember] public uint   Id   { get; private set; }
    // note this is a collision between static/non-static
    [DataMember] public string Name { get; private set; }
    //snip other properties
}

验证:

public static class Program
{
    static void Main()
    {
        var obj = FieldType.Default;
        using(var ms = new MemoryStream())
        {
            var ser = new DataContractSerializer(typeof (FieldType));
            ser.WriteObject(ms, obj);
            ms.Position = 0;
            var obj2 = ser.ReadObject(ms);
            bool pass = ReferenceEquals(obj, obj2); // true
        }
    }
}

但是请注意,如果我们只使用Id来标识要使用的实际对象,那么序列化Name似乎没有什么意义。

我建议您覆盖EqualsGetHashcode(以及==!=),以便静态对象与WCF创建的对象的相等性检查有效。

数据传输对象 (DTO) 不适用于面向对象的行为,它们纯粹是状态类。但我能理解你面临的问题。

或者,使用不同的 DTO 在域对象与上述类一起工作时发送数据。