0

I'm trying to make a class I wrote serializable. It contains several static readonly fields that never change after construction, so they don't need to be serialized. I'm having trouble finding a way to re-set them after de-serialization that doesn't weaken the access in the base class. Here's a simplified example, starting with a non-serialisable base and derived class:

internal abstract class MyBase
{
    private readonly List<int> myIntList = new List<int> ();

    internal MyBase (List<int> anIntList)
    {
        this.myIntList = anIntList;
    }
}

The derived class doesn't need to access the list, so the field can be private readonly in the base class, and the derived class set it like this:

internal sealed class MyDerived : MyBase
{
    private static readonly List<int> derivedIntList = new List<int> { 1, 2, 3 };

    internal MyDerived () : base (MyDerived.derivedIntList)
    {
    }
}

Now I want to make my derived class serializable. Since the list contents don't change, there's no need to serialize them, so I just put a DataContract attribute on both classes.

I serialize the derived class to disk like this:

private static void SeralizeDerived (string path)
{
    MyDerived derived = new MyDerived ();

    DataContractSerializer serializer = new DataContractSerializer (typeof (MyDerived));

    using (FileStream stream = new FileStream (path, FileMode.Create))
    {
        serializer.WriteObject (stream, derived);
        stream.Flush ();
    }
}

and deserialize it like this:

private static void DeserializeDerived (string path)
{
    DataContractSerializer serializer = new DataContractSerializer (typeof (MyDerived));

    using (FileStream stream = new FileStream (path, FileMode.Open, FileAccess.Read))
    {
        MyDerived derived = serializer.ReadObject (stream) as MyDerived;
        // debugger shows derived.myIntList as null, as expected
    }
}

As per the comment in DeserializeDerived, the value of derived.myIntList is null. This isn't surprising to me - I didn't ask it to be serialized, and I haven't done anything to re-create it after deserialization.

The problem is this: The only way I know to fix this is to change the access to myIntList to make it protected, and have an OnDeserialized method in the MyDerived class that (re)sets myIntList:

private void ReInitialize ()
{
    base.myIntList = MyDerived.derivedIntList;
}

[OnDeserialized]
private void OnDeserialized (StreamingContext context)
{
    this.ReInitialize ();
}

internal MyDerived () : base ()
{
    this.ReInitialize ();
}

This feels wrong - I don't want to weaken the access to the base class members, and initializing the members field-by-field in the derived constructor is more error-prone. The question is: How can I keep the same member protection but still support serialization?

  • If it's about Json deserialization, a constructor deserialization could be used: https://stackoverflow.com/questions/23017716/json-net-how-to-deserialize-without-using-the-default-constructor (for XML there is no constructor deserialization AFAIK) – Renat Jul 09 '19 at 09:55

1 Answers1

0

Passing static list into the base class constructor looks strange for me. I would say that something is wrong with inheritance here.

If you still want to use inheritance try approach with abstract property:

public abstract class Data
{
    public abstract int[] list { get;}
}

public class Data2 : Data
{
    private static readonly int[] arr = new[] {1,2,3};
    public override int[] list { get => arr; }
}
mtkachenko
  • 5,389
  • 9
  • 38
  • 68
  • Thanks mtkachenko. I should have mentioned in my example that the derived class doesn't actually need to access the list at all - it's only used in the base class. There are multiple MyDerived, and they all pass in different lists. If I read your example correctly, you've given Data2 access to the list, which I could do, but it reduces the encapsulation. – Plunder Bunny Jul 09 '19 at 12:44
  • @RichardDrysdall It's a still classic case when you need to override some property/method in derived class but main algorithm is described in base class https://sourcemaking.com/design_patterns/template_method . In my example I force derived class to override abstract `list` property because it's required for base class. Of course you can use `protected` instead of `public`. There is no problems with encapsulation. – mtkachenko Jul 09 '19 at 12:54
  • Thank you mtkachenko - I hadn't realised the connection to templating. I've tried your example and it works perfectly. I will try to see if I can re-implement my classes using templates, but at-least I have something that works for now. – Plunder Bunny Jul 10 '19 at 06:47
  • the readonly array is the key point here, and of course hard-coded array. But how to solve for example json deserialization with array of unknow members ? – Mitja Gustin Jul 06 '20 at 14:03