简体   繁体   English

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

[英]DataContractSerializer and immutable types. Deserialising to a known object instance

I have a class that is effectively a object based enum. 我有一个实际上是基于对象的枚举的类。 There is a static set of objects exposed by the class, and everything uses these same instances. 该类公开了一组静态对象,并且所有对象都使用这些相同的实例。 eg (Note the private constructor) 例如(注意私有构造函数)

[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
}

This works great until I have to serialise across WCF. 这非常有效,直到我必须在WCF中进行序列化。 The DataContractSerializer creates new objects by bypassing the constructor. DataContractSerializer绕过构造函数创建新对象。 This results in a valid FieldType object, but it is a new instance that is not one of my static instances. 这会产生一个有效的FieldType对象,但它是一个不是我的静态实例之一的新实例。 This makes reference comparisons against the known static values fail. 这使得与已知静态值的参考比较失败。

Is there any way to override the serialisation behaviour for a class so that I create the object instance instead of populating an instance supplied to me? 有没有办法覆盖类的序列化行为,以便我创建对象实例而不是填充提供给我的实例?

I suspect you can do: 怀疑你能做到:

[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
}

Validated: 验证:

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
        }
    }
}

Note, however, that there seems little point serializing the Name if we only use the Id to identify the real object to use. 但请注意,如果我们仅使用Id来标识要使用的真实对象,那么序列化Name似乎没什么意义。

I suggest you override Equals and GetHashcode (and == and != ) so that your equality check of the static object with the one created by WCF works. 我建议你重写EqualsGetHashcode (以及==!= ),以便用WCF创建的静态对象进行等式检查。

Data Transfer Objects (DTOs) are not meant for Object Oriented behaviour and they are purely state classes. 数据传输对象(DTO)不适用于面向对象的行为,它们纯粹是状态类。 But I can understand the problem you are facing. 但我能理解你所面临的问题。

Alternatively use a different DTO for sending the data across while your domain objects work with the class above. 或者,当您的域对象与上面的类一起使用时,使用不同的DTO来发送数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM