如何序列化可能只有唯一实例的类

时间:2018-07-22 16:32:32

标签: c# serialization

我有一个只能包含如下唯一实例的类。这使得引用相等足以等同于对象。

虽然序列化很简单,但是反序列化有点棘手,因为不能像下面我所做的那样分配给this。如果仍然只想维护类的唯一实例,该如何反序列化对象?

public sealed class MyClass :
    ISerializable,
    IXmlSerializable
{
    private static readonly Dictionary<string, MyClass> Cache;

    static MyClass() { /* build cache, use private constructor */ }

    private MyClass(string name)
    {
        this.Name = name;
    }

    public string Name { get; }

    public static MyClass Parse(string from)
        => Cache[from];

    public void GetObjectData(SerializationInfo info, StreamingContext context)
        => throw new NotImplementedException();

    public XmlSchema GetSchema() => null;

    public void ReadXml(XmlReader reader)
    {
        reader.ReadStartElement();
        this = Parse(reader.ReadContentAsString());
        reader.ReadEndElement();
    }

    public MyClass(SerializationInfo info, StreamingContext context) 
        => this = Parse(info.GetString(nameof(this.Name)));

    public void WriteXml(XmlWriter writer)
        => throw new NotImplementedException();
}

1 个答案:

答案 0 :(得分:1)

一般而言,解决此问题的方法是在对象图序列化期间,使用serialization surrogate mechanism替换单身人士,其中,单身人士被不包含任何内容的Data Transfer Object (DTO)替换除了单身人士的标识符。然后,随后在对图形进行反序列化时,首先对DTO进行反序列化,然后通过查找将其替换为相应的单例。

例如,如果您的MyClass如下所示:

public sealed class MyClass
{
    private static readonly Dictionary<string, MyClass> Cache;

    static MyClass()
    {
        Cache = new Dictionary<string, MyClass>()
        {
            { "one", new MyClass("one") { OtherRuntimeData = "other runtime data 1" } },
            { "two", new MyClass("two") { OtherRuntimeData = "other runtime data 2" } },
        };
    }

    // XmlSerializer required parameterless constructor.
    private MyClass() => throw new NotImplementedException();

    private MyClass(string name) => this.Name = name;

    public string Name { get; }

    public string OtherRuntimeData { get; set; }

    public static MyClass Parse(string from) => Cache[from];

    public static IEnumerable<MyClass> Instances => Cache.Values;
}

然后仅包含Name的DTO看起来像:

public sealed class MyClassDTO
{
    public string Name { get; set; }

    public static implicit operator MyClassDTO(MyClass obj) => obj == null ? null : new MyClassDTO { Name = obj.Name };

    public static implicit operator MyClass(MyClassDTO dto) => dto == null ? null : MyClass.Parse(dto.Name);
}

是否注意到implicit operator用于在DTO和原件之间进行转换?这样可以更轻松地将代理注入序列化图中。

但是,不幸的是,在所有常用的.Net序列化程序中,没有标准的方法来实现注入序列化替代DTO。每个都有其自己的独立机制(或根本没有机制)。分解它们:

相关问题