• 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

    • 26200 25H2 is shaping up to be a very solid and stable release, hardly any issues with it here on 3 different configurations of computers
    • Samsung Galaxy S25+ 512GB is $220 off for powerful AI and a pro-grade camera by Paul Hill Are you in the market for a premium Samsung phone? If so, check out the Samsung Galaxy S25+ with 512GB of storage. It’s on Amazon right now for just $899, down 20% from its $1,199.99 list price, representing a significant $220.99 saving. This unlocked device is marked as the number 1 new release by Amazon in the Cell Phones category. If you’re interested, act fast as it’s a limited-time deal. The Galaxy S25+ comes packed with AI features under the umbrella of Galaxy AI. Capabilities include Multiple Tasks with One Ask which brings Google Gemini integration for multi-app commands, Now Brief which proactively gives you information you need to start the day, Audio Eraser to remove distracting sounds from your videos, and advanced portrait features. Powering these features is the Qualcomm SM8750-AB Snapdragon 8 Elite (3 nm) processor which handles all sorts of tasks efficiently including gaming, translation, and photo editing. Alongside the processor is 512GB of storage and 12GB of RAM. The S25+ uses a 6.7-inch QHD+ ProScaler Display which delivers vibrant visuals thanks to its use of Dynamic AMOLED 2X with 3,120 x 1,440 resolution and 120Hz refresh rate. Regarding camera setup, the S25+ has an AI camera with 50MP main sensor, 12MP ultrawide, and 10MP telephoto with OIS. There is also a 12MP front camera. This camera setup is capable of 8K video recording, which is impressive. Finally, you get long battery life with the 4,900 mAh and 45W fast charging support so you don’t need to wait long for it to recharge. If you’re an Android user looking to upgrade to a flagship phone without paying the full price, this deal is for you. If you have an eligible phone to trade in, there is an option to do so to claim up to $725 on the upgrade with Amazon.com Gift Card credit. If you’re excited by AI, but your current phone doesn’t support many AI features, this phone could also be a smart choice. Its display is also great for media consumption, and the processor is robust. Finally, if you have a lot of files to store, the 512GB of storage should be ample. Samsung Galaxy S25+ (Icyblue): $899 (Amazon US) / MSRP $1,199.99 This Amazon deal is US-specific and not available in other regions unless specified. If you don't like it or want to look at more options, check out the Amazon US deals page here. Get Prime (SNAP), Prime Video, Audible Plus or Kindle / Music Unlimited. Free for 30 days. As an Amazon Associate, we earn from qualifying purchases.
    • Sniffnet 1.4.0 by Razvan Serea Sniffnet is a network monitoring tool to help you easily keep track of your Internet traffic. Whether you want to gather statistics, or you need to inspect more in depth what's going on in your network, this app will get you covered. Sniffnet is a technical tool, but at the same time it strongly focuses on the overall user experience: most of the network analyzers out there are cumbersome to use, while one of Sniffnet's cornerstones is to be usable with ease by everyone. Furthermore, Sniffnet is completely free and open-source, dual-licensed under MIT or Apache-2.0: if you are interested you can find the full source code on GitHub. Last but not least, this application is totally developed in Rust: a modern programming language to build efficient and reliable software, emphasizing performance and safety. Sniffnet key features choose a network adapter of your PC to inspect select a set of filters to apply to the observed traffic view overall statistics about your Internet traffic view real-time charts about traffic intensity keep an eye on your network even when the application is minimized export comprehensive capture reports as PCAP files identify 6000+ upper layer services, protocols, trojans, and worms find out domain name and ASN of the hosts you are exchanging traffic with identify connections in your local network discover the geographical location of the remote hosts save your favorite network hosts inspect each of your network connections in real time set custom notifications to inform you when defined network events occur choose the style that fits you the most, including custom themes support ... and more! Sniffnet 1.4.0 changelog: New features Import PCAP files (#795 — fixes #283) Enhanced notifications (#830 — fixes #637) Donut chart reporting overall traffic statistics (#756 — fixes #687) Added support for ARP protocol (#759 — fixes #680) Identify and tag unassigned/reserved "bogon" IP addresses (#678 — fixes #209) Show data agglomerates in Inspect page table (#684 — fixes #601) Added Traditional Chinese (Taiwan) translation 🇹🇼 (#774) Added Indonesian translation 🇮🇩 (#611) A Docker image of Sniffnet is now available (#735) Improvements Added new themes A11y (Night) and A11y (Day) based on palettes optimized for Accessibility (#785 — fixes #786) Do not apply new notification thresholds while user is typing them (#777 — fixes #658) Show more information when domain name is short (#720 — fixes #696) Avoid directory traversal when selecting file name for PCAP exports (#776 — fixes #767) Add icon to window title bar (#719 — fixes #715) Update footer buttons and links (#755 — fixes #553) Handle errors to reduce the number of possible crash occurrences (#784) Updated some of the existing translations to v1.3: Portuguese (#690) Ukrainian (#692) Spanish (#805) Fixes Fix crates.io package for Windows (#718 — fixes #681) Fix crash when inserting characters longer than one byte in the text input for byte threshold notification setting (#747 — fixes #744) Remove pre-uninstall script on Linux (fixes #644) Fix typo in Russian translation (fixes #730) Minor fix to service determination algorithm in case of multicast and broadcast traffic Download: Sniffnet 64-bit | Sniffnet 32-bit ~15.0 MB (Open Source) Link: Sniffnet Home Page | Other operating systems | Screenshot Get alerted to all of our Software updates on Twitter at @NeowinSoftware
    • Anker announces global recall of five power bank models over fire safety risks by Aditya Tiwari The Chinese electronics brand, Anker Innovations, known for its mobile accessories and power banks has announced a voluntary global recall of five power bank models. The decision comes after the company spotted a potential fire hazard issue with lithium-ion battery cells from a particular vendor. Anker said that it put up a series of quality checks to detect manufacturing issues early in the production cycle, which include component level-audits and supplier testing. The company assured that "while the likelihood of malfunction is considered minimal, out of an abundance of caution, we have decided to initiate a voluntary global recall of several Anker power bank models." Here's the list of the Anker Power Bank models chosen for the global recall: Model A1257 - Anker Power Bank (10K, 22.5W) Model A1647 - Anker Power Bank (20,000mAh, 22.5W, Built-In USB-C Cable) Model A1652 - Anker MagGo Power Bank (10,000mAh, 7.5W) Model A1681 - Anker Zolo Power Bank (20K, 30W, Built-In USB-C and Lightning Cable) Model A1689 - Anker Zolo Power Bank (20K, 30W, Built-In USB-C Cable) If you think you own one of the affected power banks, you can check the model number located on the back or side of your power bank. After that, you can fill out the recall form to start the process and verify the serial number of your affected device. If your power bank is eligible for the recall, you can either get a replacement or receive a gift card for use on the Anker website. It's not offering any refunds in the US at the moment. Anker advises that you should stop using an impacted power bank immediately even if the device functions normally right now. A unit confirmed for the recall could pose the risk of overheating, melting, smoke, or fire. This is the second major recall from Anker in the same month after more than a dozen reports of fires and explosions. The company previously recalled over one million Anker PowerCore 10000 (A1263) power bank units, citing fire safety risks due to a potential issue with the Lithium-ion battery. These power banks were sold in the US from January 1, 2016 through December 31, 2022.
  • Recent Achievements

    • Collaborator
      Mighty Pen went up a rank
      Collaborator
    • Week One Done
      emptyother earned a badge
      Week One Done
    • Week One Done
      DarkWun earned a badge
      Week One Done
    • Very Popular
      valkyr09 earned a badge
      Very Popular
    • Week One Done
      suprememobiles earned a badge
      Week One Done
  • Popular Contributors

    1. 1
      +primortal
      567
    2. 2
      +FloatingFatMan
      189
    3. 3
      ATLien_0
      176
    4. 4
      Skyfrog
      112
    5. 5
      Xenon
      110
  • Tell a friend

    Love Neowin? Tell a friend!