• 0

[C#] Serialization of abstract class


Question

10 answers to this question

Recommended Posts

  • 0

What benefit do you see of serialising the just the properties of the abstract type? You can infact do this I guess, if you define deserialisation constructors. For instance, we have a type:

public abstract class Person : ISerializable
{
  #region Constructor
  public Person() { }

  protected Person(SerializationInfo info, StreamingContext context)
  {
    Forename = info.GetString("forename");
    Surname = info.GetString("surname");
  }
  #endregion

  #region Properties
  public string Forename { get; set; }
  public string Surname { get; set; }
  #endregion

  #region Methods
  public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
  {
    info.Add("forenamerename);
    info.Add("surnamername);
  }
  #endregion
}

This type implements the ISerializable interface, and also provides a deserialisation constructor with signature (SerializationInfo, StreamingContext). Obviously we can't instantiate this type, but we could define a type which implements this, and is deserialized:

public class Employee : Person
{
  #region Constructors
  public Employee() { }

  protected Employee(SerializationInfo info, StreamingContext context) : base(info, context)
  {
    // Additional deserialisation here.
  }
  #endregion

  #region Properties
  public string Department { get; set; }
  #endregion
}

BinaryFormatter formatter = new BinaryFormatter();
Employee emp = (Employee)formatter.Deserialize(<stream>);

The Employee type does't implement any specific serialisation of its own properties, so when serialising, it will only serialise the properties of the base type. We could of course serialise our local properties too:

public class Employee : Person
{
  #region Constructors
  public Employee() { }

  protected Employee(SerializationInfo info, StreamingContext context) : base(info, context)
  {
    Department = info.GetString("department");
  }
  #endregion

  #region Properties
  public string Department { get; set; }
  #endregion

  #region Methods
  public override void GetObjectData(SerializationInfo info, StreamingContext context)
  {
    base.GetObjectData(info, context);
    info.Add("department }
  #endregion
}

The net result is, although the data of the abstract type is serialised, its actually the derived type that is serialised in the stream.

Edited by Antaris
Removed incorrect logic.
  • 0
  On 24/02/2010 at 17:45, Antaris said:

What benefit do you see of serialising the just the properties of the abstract type? You can infact do this I guess, if you define deserialisation constructors. For instance, we have a type:

public abstract class Person : ISerializable
{
  #region Constructor
  public Person() { }

  protected Person(SerializationInfo info, StreamingContext context)
  {
    Forename = info.GetString("forename");
    Surname = info.GetString("surname");
  }
  #endregion

  #region Properties
  public string Forename { get; set; }
  public string Surname { get; set; }
  #endregion

  #region Methods
  public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
  {
    info.AddValue("forename", Forename);
    info.AddValue("surname", Surname);
  }
  #endregion
}

This type implements the ISerializable interface, and also provides a deserialisation constructor with signature (SerializationInfo, StreamingContext). Obviously we can't instantiate this type, but we could define a type which implements this, and is deserialized:

public class Employee : Person
{
  #region Constructors
  public Employee() { }

  protected Employee(SerializationInfo info, StreamingContext context) : base(info, context)
  {
    // Additional deserialisation here.
  }
  #endregion

  #region Properties
  public string Department { get; set; }
  #endregion
}

BinaryFormatter formatter = new BinaryFormatter();
Employee emp = (Employee)formatter.Deserialize(<stream>);

The Employee type does't implement any specific serialisation of its own properties, so when serialising, it will only serialise the properties of the base type. We could of course serialise our local properties too:

public class Employee : Person
{
  #region Constructors
  public Employee() { }

  protected Employee(SerializationInfo info, StreamingContext context) : base(info, context)
  {
    Department = info.GetString("department");
  }
  #endregion

  #region Properties
  public string Department { get; set; }
  #endregion

  #region Methods
  public override void GetObjectData(SerializationInfo info, StreamingContext context)
  {
    base.GetObjectData(info, context);
    info.AddValue("department");
  }
  #endregion
}

The net result is, although the data of the abstract type is serialised, its actually the derived type that is serialised in the stream. If I am following what I think you want to do, you want something like this:

Person person = (Person)formatter.Deserialize(<stream>);

I am not sure if that would work, simply because the Person type cannot be instantiated, because it is abstract. And doing this:

Person person = (Person)(Employee)formatter.Deserialize(<stream>);

... doesn't provide the clean separation of concerns you want.

Well, the reason for using an abstract class is because I have a composite model in my program so I have a collection of some types that implement an interface so I can't serialize the collection. I want the collection to have interface because I want it to be as generic as possible. In the end I settled for an abstract class that implements a custom interface and ISerializable. When I serialize the class I serialize it as the abtrast type and when deserialize I cast it to my custom interface.

About what you said.To further clarify things, you can deserialize derived classes from an abtrast type,even if you serialized it as the abstrast class, because when you serialize to a binary format it also adds metadata to the file so it knows the real type of the serialized class and so it can call the appropiate constructor. I've tested and confirmed this after quite some research. You can even open the file in notepad and you can see some readable things :).

  • 0
  Quote
About what you said.To further clarify things, you can deserialize derived classes from an abtrast type,even if you serialized it as the abstrast class, because when you serialize to a binary format it also adds metadata to the file so it knows the real type of the serialized class and so it can call the appropiate constructor. I've tested and confirmed this after quite some research. You can even open the file in notepad and you can see some readable things

But if you have an instance of an abstract type, its actually an instance of a derived type, so what gets serialised is the derived type, no? What I mean is, when you call any of the methods (Serialize, Deserialize), at no point do you express the type, e.g. typeof(Person) [as per my example]. Internaly if the BinaryFormatter makes a call to GetType(), the derived type will be returned, not the abstract type?

  • 0
  On 24/02/2010 at 19:46, Antaris said:

But if you have an instance of an abstract type, its actually an instance of a derived type, so what gets serialised is the derived type, no? What I mean is, when you call any of the methods (Serialize, Deserialize), at no point do you express the type, e.g. typeof(Person) [as per my example]. Internaly if the BinaryFormatter makes a call to GetType(), the derived type will be returned, not the abstract type?

Well,when I serialize the abstract classes, i use typeof(List<abstract class name>) . Also I don't serialize the original List<myinterface> but create a separate list and cast the members to the abstract type. This is probably a very bad practive and completely useless in real programming but it's nice for an exercise. So i serialize the entire collection, like this serialization_info_instace.add("tag",list<abstract_type_name>_instancealize you don't use typeof, but only when you deserialize. Collections with serializable members are also serializable.

I even created a special class to check the serialization of abstract classes. In my class I have an derived class instance member explicitly declared as derived, so it includes the entire type ierarchy when I serialize,. When I deserialize i use typeof(abstract class) with the GetValue method and not GetString as in the example.I even tried declaring the derived class instance as an abstract class member and it still worked so it's the same as the previous case. So I guess it doesn't matter how you serialize/deserialize your classes because it will always include the full type ierarchy when serializing. As long as you deserialize to something that is in the type ierarchy it will work. But only with classes. I tried to serialize something as an interface and I got an exception although I can deserialize something as an interface using typeof(myinterfacename) as a paramenter to the GetValue method.

Hope I was clear enough and didn't make any mistakes :happy: .

  • 0

Well, I think I'm getting confused over exactly what you want to achieve. Using my example from before, if I serialise a derived type, and then deserialise it, we can see that it is actually the derived type that is deserialised before we cast it back to the abstract type:

post-92970-12670880091169_thumb.png

In the same sense, I have an example type which implements an abstract collection: List<Person> (read: not List<Employee>):

[Serializable]
public class PeopleSet : ISerializable
{
    #region Constructors
    public PeopleSet()
    {
        People = new List&lt;Person&gt;();
    }

    protected PeopleSet(SerializationInfo info, StreamingContext context)
    {
        People = (List&lt;Person&gt;)info.GetValue("list", typeof(List&lt;Person&gt;));
    }
    #endregion

    #region Properties
    public List&lt;Person&gt; People { get; private set; }
    #endregion

    #region Methods
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("list", People);
    }
    #endregion
}

Now, if we are explicitly using typeof(List<Person>) when adding the list to the SerializationInfo, but when we deserialise the PeopleSet type, the list is deserialised and cast back to List<Person>, but the item contained is still the derived type I added before:

post-92970-12670882362371_thumb.png

The thing I think you will fall into problems with, is if you are trying to deserialise purely as the abstract type, where the derived type is not available. I.e., you have a library with your abstract type, which is used throughout, but the derived type is only available during serialisation. This wouldn't work, as when you attempt to deserialise when the derived type is not available, an Exception will be thrown.

  • 0
  On 25/02/2010 at 09:00, Antaris said:

Well, I think I'm getting confused over exactly what you want to achieve. Using my example from before, if I serialise a derived type, and then deserialise it, we can see that it is actually the derived type that is deserialised before we cast it back to the abstract type:

post-92970-12670880091169_thumb.png

In the same sense, I have an example type which implements an abstract collection: List<Person> (read: not List<Employee>):

[Serializable]
public class PeopleSet : ISerializable
{
    #region Constructors
    public PeopleSet()
    {
        People = new List&lt;Person&gt;();
    }

    protected PeopleSet(SerializationInfo info, StreamingContext context)
    {
        People = (List&lt;Person&gt;)info.GetValue("list", typeof(List&lt;Person&gt;));
    }
    #endregion

    #region Properties
    public List&lt;Person&gt; People { get; private set; }
    #endregion

    #region Methods
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("list", People);
    }
    #endregion
}

Now, if we are explicitly using typeof(List<Person>) when adding the list to the SerializationInfo, but when we deserialise the PeopleSet type, the list is deserialised and cast back to List<Person>, but the item contained is still the derived type I added before:

post-92970-12670882362371_thumb.png

The thing I think you will fall into problems with, is if you are trying to deserialise purely as the abstract type, where the derived type is not available. I.e., you have a library with your abstract type, which is used throughout, but the derived type is only available during serialisation. This wouldn't work, as when you attempt to deserialise when the derived type is not available, an Exception will be thrown.

Sorry for making such a mess out this. I managed to serialize the classes just as I wanted though as you pointed I can't always do like this because I don't always have acces to the type. I believe this is important to remember because changing this requires a lot of code rewriting. So when making a serious application you need to know about this from the start. I still needed some clarifications but now I pretty much understand the concept .

  • 0

Don't worry about it, it's all the fun of development. If you want to truly break the dependancy on the derived type, you could implement some sort of proxy object which implements your abstract class or interface. You won't be able to use binary serialisation, but I can't see any reason why you could use xml serialisation and custom reconstruction.

  • 0
  On 26/02/2010 at 08:38, Antaris said:

Don't worry about it, it's all the fun of development. If you want to truly break the dependancy on the derived type, you could implement some sort of proxy object which implements your abstract class or interface. You won't be able to use binary serialisation, but I can't see any reason why you could use xml serialisation and custom reconstruction.

I did break dependency in a way. I have a core assembly in which I have defined my interfaces and abstract types. I reference that assembly in my project and build on top of it. And in my program I inspect a folder called plugins for additional assemblies. It inspecs each assembly for derived types from my interfaces and abstract classes and loads them into a list. I use that list to create objects of those types. My convention is that every derived class from my abstract classes and interfaces should have a constructor that takes certain parameters so that I can instantiate those classes for sure so I can add new objects of those custom types to my application from it's GUI. I also use that list of custom types to deserialize my objects from binary files. I need to make a custom binder and set it to the formatter. The custom binder searches the list of types for the desired type.

And not just that, I was playing with nested classes. Each nested class if derived from another abstract attribute class, represents an attribute that I can set to object of those types or types derived from it. I don't implement interfaces directly, but rather create an abstract class that maps the methods and properties. And I use that class to derive from it. So it's very easy to add new attributes to my classes. (not attributes that you put in [] to mark the code with special properties, but rather custom ones that are completely unrelated to those). So my main abstract class has a property that gets or sets a list of attributes which also exists in the interface that it implements. But also it has come concrete classes that denote general attributes that apply to all the classes derived from it. I really like how this makes things really logical and it's very easy to extend and customize my program with additional assemblies. It's really amazing what you can do with .net.

I researched and came with another idea, to add support for custom sources that contain actual code. It will probably have another folder called sources. I will compile them at runtime and inspect them for my desired types. This will make adding custom content to my application even easier because you won't even need to compile the code because my application will do it for you.

This is imo one of the best parts of programming :D .

  • 0

There are also SerializationSurrogates which can be used to serialize instances of classes (sealed, perhaps, or otherwise unmodifiable) that are not normally serializable. Of course, you only have access to the public members in this situation, unless of course you use reflection.

I had to do this because in .net 1.1 Microsoft left 3 Exception classes without the ISerializable interface.

Key things to take away here:

1) If B is instance of A -> List<B> is instance of List<A>

2) You cannot have an instance of an abstract type

3) You don't need to downcast.

public abstract class A {}
public class B : A{}

.... 
public A MakeA() { return new B(); }
public List&lt;A&gt; MakeAs() { return new List&lt;B&gt;(); }

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Posts

    • I was thinking, and was going to post, almost exactly what @Brandon Hjust posted!   WARNING: possible spoilers I also love these videos she makes:   
    • The Witcher 4 tech demo shows off the RPG in action on a base PS5 at 60FPS by Pulasthi Ariyasinghe Epic Games' The State of Unreal 2025 keynote happened today, giving a look at its latest improvements to the Unreal Engine, and other developments and partnerships that the Fortnite maker is involved in. The show kicked things off with a bang, giving fans a live on-stage presentation of The Witcher 4 alongside developer CD Projekt RED. Seen below, the tech demo of the hugely anticipated RPG features the new trilogy's protagonist, Ciri, as she explores Kovir, the new mainland that CD Projekt is letting players explore. Following a monster trail, Ciri and her horse Kelpie go through mountains and forests before reaching the town of Valdrest. Surprisingly, the demo is said to be running on a base PlayStation 5, and it even manages to stay at 60 frames per second despite the stunning visuals. The studio said that the technologies it is using will benefit all platforms that it is targeting for the RPG. The Witcher 4 is coming to PC, Xbox Series X|S, and PlayStation 5 consoles, though a release date has not been revealed yet. "We started our partnership with Epic Games to push open-world game technology forward. To show this early look at the work we’ve been doing using Unreal Engine running at 60 FPS on PlayStation 5, is a significant milestone — and a testament of the great cooperation between our teams," said joint CEO of CD Projekt RED Michał Nowakowski. "But we're far from finished. I look forward to seeing more advancements and inspiring technology from this partnership as development of The Witcher 4 on Unreal Engine 5 continues." The demo showcases tech like Unreal Animation Framework, Nanite Foliage rendering, MetaHuman technology with Mass AI crowd scaling, and more from Epic Games' latest engine developments. Unreal Engine 5.6 is said to include many of these improvements, letting any developer use the tools on their games for better visuals and optimizations.
    • The same guy who promised AGI in a year, and later he said: yes, but no really.
    • Microsoft and security. 🤣 One day, I received a big bill from Microsoft. I checked the log, and the log (not Azure Premium) only lasted a few weeks, so I was unable to find any information about what happened. I checked the bill, and it showed me that somebody (no information) created a big virtual machine for many weeks, then finally deleted it. I contacted support, and they told me that I will get my money back. 8 months later, it never happened. However, the invoices show some weird movements. They indeed did the reverse, but only in the system, but my CC never got the money back. I had a fight with my bank, and my bank finally showed me records that Microsoft never tried to return my money. What happened, who hacked it, and how? Microsoft was unable to answer. My account had a DFA. Conclusion: I should have reported it to my CC.
  • Recent Achievements

    • Week One Done
      Adam Todd earned a badge
      Week One Done
    • Contributor
      Ed B went up a rank
      Contributor
    • One Month Later
      moporcho earned a badge
      One Month Later
    • One Month Later
      Parotel earned a badge
      One Month Later
    • Reacting Well
      Cryptecks earned a badge
      Reacting Well
  • Popular Contributors

    1. 1
      +primortal
      202
    2. 2
      snowy owl
      144
    3. 3
      ATLien_0
      133
    4. 4
      Xenon
      120
    5. 5
      +FloatingFatMan
      106
  • Tell a friend

    Love Neowin? Tell a friend!