1

I have a class that can only have unique instances as below. This makes reference equality sufficient to equate objects.

While serialization is simple, deserialization is a bit tricky as one cannot assign to this as i have done below. How do i deserialize objects if still want to maintain only unique instances of the class?

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();
}
Quantifeye
  • 261
  • 2
  • 10
  • If you're able to change your class code, I would highly suggest to replace your class by three different ones; one for serialization/deserialization, one for caching, and another one for the class that you need to cache. Your class here makes different responsibilities which makes it more complicated. – Abdullah Dibas Jul 22 '18 at 17:06

1 Answers1

1

Generally speaking, the way to deal with this issue is, during object graph serialization, to replace the singleton(s) using a serialization surrogate mechanism, wherein the singleton is replaced with a Data Transfer Object (DTO) that contains nothing other than an identifier for the singleton. Then, later, as the graph is being deserialized, the DTO is initially deserialized, then replaced with the corresponding singleton via a lookup.

For instance, if your MyClass looks as follows:

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

Then a DTO containing only the Name would look like:

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

Notice the implicit operator for converting between the DTO and the original? That will make it easier to inject the surrogate into the serialization graph.

Unfortunately, however, there is no standard way to implement injection of a serialization surrogate DTO in all of the commonly used .Net serializers. Each has its own separate mechanism (or no mechanism at all). To break them down:

dbc
  • 104,963
  • 20
  • 228
  • 340