• 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

    • It's a Dell color laser printer. Back when Microsoft kept having these security flaws with the print spooler and recommending you disable it I just got in the habit of it, and I use it so rarely now I just leave it disabled in case another flaw pops up.
    • KDE makes progress toward full Wayland session restore in Plasma 6.5 by David Uzondu In the latest issue of This Week in Plasma, the development team, as usual, brings news of ongoing work for the desktop environment. While KDE continues to polish the recently released Plasma 6.4, work has already taken off on the next major version of Plasma, 6.5. A significant step forward is being made on Wayland session restoration; the xx-session-management-v1 restore protocol has been implemented in Qt 6.10, which means KDE applications and Plasma itself can soon start using it to finally bring proper session restore to Wayland. For more immediate user-facing changes in Plasma 6.5, the Welcome Center application now teaches you about the many available keyboard shortcuts, as well as "what the heck the 'Meta' key is." For those who frequently work remotely, Plasma's built-in RDP server now supports syncing clipboard text between the client and server. The clipboard also received another useful feature, letting you copy the QR code for an item, not just view it. The team is also addressing smaller usability issues across the desktop, including fixes that will land in point releases for Plasma 6.4. Spectacle, for example, will no longer show a ghostly semi-transparent version of its menus in screenshots. The New! badge, which was introduced in Plasma 6.4 is now easier to read with better colors. Other notable UI improvements include: A new button on the "missed notifications" pop-up that lets you view what you actually missed. The Networks widget is now much better at telling you what it is doing, like when it is "looking for wireless networks". Inertial scrolling with touchpads is now active in all QtQuick-based KDE software. As always, the KDE team spent the week squashing bugs across various versions of Plasma and related software. Plasma 6.4.1, which went live this Tuesday, addressed several crashes in the desktop portal implementations and patched the open/save dialog, where apps could insert extra UI elements in the wrong places. It also resolved a recent performance regression affecting some games and fixed a strange issue with drawing tablets where the pointer could disappear when two were connected in different modes. 6.4.1 tackled a particularly odd hardware-specific problem with Samsung Odyssey G5 monitors endlessly turning on and off because of a faulty DDC implementation. KDE responded by blacklisting the device. In addition to that, the update fixed an accessibility regression in Discover, corrected an issue where deleting a favorited app left behind a ghost item you couldn't un-favorite, and restored the Window List widget's ability to minimize windows. As for fixes planned for 6.4.2, here's the full list as outlined by the Plasma team: Fixed a case where System Settings' Flatpak App Permissions page could cause the whole app to crash. Fixed an issue that could sometimes cause Plasma to go back to sleep again right after waking up, when the "Sleep then hibernate" setting is in use. The appearance of text labels in Folder View pop-ups is once again correct. You're no longer erroneously prompted to authenticate for a Wireguard VPN whose credentials are already stored in KWallet, and the wallet is set up to automatically open at login. Fixed an issue in the KDE desktop portal's screenshot implementation that prevented the delay setting from taking effect. Missing app backends listed in Discover's Settings page once again show the correct names. The brightness level shown on System Settings' Display & Monitor page now matches the one shown in Plasma. Fixed an issue that caused the panel to have too much space in it until restarting Plasma if you stop displaying the date on a horizontally-laid-out Digital Clock widget. The older Plasma 6.3.6 fixed video stuttering on variable-refresh-rate screens and patched the Weather Report widget after Environment Canada changed its data format. You can check out the full update on the official KDE Blog.
    • I'm happy with my setup. I have a raid 1 two drive Synology setup that I backup with Time Machine to every so often. What is your setup at home? 
    • Enterprise support (ex. Premier Support) is a bit different cup of coffee. I am not saying it is great, but they have SLAs and I have received solution within hours and even patches within 3 days.
    • Thank you, for not reading the comments above and just reading the root comment... it is just like reading only the headline of an article.
  • Recent Achievements

    • One Year In
      TsunadeMama earned a badge
      One Year In
    • Week One Done
      shaheen earned a badge
      Week One Done
    • Dedicated
      Cole Multipass earned a badge
      Dedicated
    • Week One Done
      Alexander 001 earned a badge
      Week One Done
    • Week One Done
      icecreamconesleeves earned a badge
      Week One Done
  • Popular Contributors

    1. 1
      +primortal
      569
    2. 2
      ATLien_0
      187
    3. 3
      +FloatingFatMan
      184
    4. 4
      Skyfrog
      112
    5. 5
      Som
      108
  • Tell a friend

    Love Neowin? Tell a friend!