• 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

    • https://deadline.com/2025/06/spaceballs-2-casts-rick-moranis-bill-pullman-keke-palmer-1236431204/
    • Microsoft updates default app choices for Windows 10 and the Curl tool with build 19045.6029 by Sayan Sen Microsoft has rolled out a new Windows 10 release preview build today for Insiders flighting the channel. The new build, 19045.6029, has some new changes and improvements. The biggest highlight is related to default app choices for the EEA (European Economic Area) region. Microsoft writes: "We are rolling out some small changes in the EEA region for default browsers via the Set default button in Settings > Apps > Default apps: Additional file and link types will be set for the new default browser, if it registers them. The new default browser will be pinned to the Taskbar and Start menu unless you choose not to pin it by clearing the checkboxes. There is now a separate one-click button for browsers to change your .pdf default, if the browser registers for the .pdf file type." Microsoft has also updated the curl command line tool with "most recent stable version is 8.14.1". Aside from those, remote Component Object Model (COM) activation that were failing with 0x8001011 error code has been fixed. And there are several other changes too. The full changelog is given below: [Mobile Operator Profiles] Updated: Country and Operator Settings Asset (COSA) profiles. [App Platforms and Frameworks] Fixed: An issue affecting Component Object Model (COM) functionality on Windows platforms, where remote COM activations were failing with error 0x8001011. Upgraded the version of the curl tool included in Windows to v8.13.0. [Authentication Platform] Fixed: An issue affecting the device registration in Entra ID Windows Account Manager (WAM) plugin. [Input and Composition] Fixed: An issue affecting the complete removal of unused language packs and Feature on Demand (FOD) packages. This led to unnecessary storage use and increase in Windows Update installation time. [Print and Peripherals] Fixed: An issue affecting USB-connected Multi-Function printers with dual protocol interfaces, where scanning may fail and prevent use of the OS’s built-in scanning functionality. [Start Menu] Fixed: An issue causing jump lists to disappear from the Start Menu. Fixed: An issue where the Start Menu was not starting after installing an update. [Settings] Fixed: Settings > System > About unexpectedly shows version 2009 instead of version 22H2. [Servicing] Fixed: An issue where Kiosk devices using the ForceAutoLogon configuration and Shift Override might stop responding with a blue screen after being locked and unlocked by support administrators. [File Server] Fixed: An issue where the system may hang when acknowledging an Oplock break on resources located on SMB shares. You can find the official blog post here on Microsoft's website.
    • This is a liability problem. They aren't simply going to trust that you aren't the driver. I think if they really wanted to they could do something similar to key fobs where they only work if they are in the proximity of the driver's seat. As already pointed out by a Random Stranger, simply having your passenger hit the play button doesn't make it any less distracting for the driver.
    • Windows 11 gets improved app defaults settings and Windows Share in build 22631.5545 by Taras Buria Windows 11 build 22631.5545 is now available for download in the Release Preview Channel of the Windows Insider Program. The update is a pretty minor one, but it still packs some important changes, such as improvements for app defaults in the Settings app, Windows Share enhancements, and a few fixes here and there. With build 22631.5545, Microsoft is giving users in the EEA region more control over default apps in Windows 11, particularly for browser defaults. Now, browser defaults support additional file and link types. Your default browser now pins itself to the taskbar (you can turn this option off), plus you can change your typical PDF viewer with one click (if the browser of choice supports PDF handling). As for Windows Share improvements, the sharing window now includes a preview of the link that you are about to send to someone. The rest of the changelog includes various fixes: [Audit] Fixed: An issue with auditing privilege use created too many security event logs. These logs filled up the system drive and prevented users from signing in. [Authentication] Fixed: This update fixes an issue where domain-joined machines running Windows 11 22H2 or 23H2 couldn’t update their account passwords on Windows Server 2025 domain controllers, which led to trust relationship issues. [Country and Operator Settings Asset (COSA)] Fixed: This update brings profiles up to date for certain mobile operators. [Display Kernel] Fixed: An issue that prevented Remote Desktop Protocol (RDP) connections until you restarted your device. [Network file sharing] Fixed: This update fixes an issue where workstations and servers might stop responding when connecting to resources located on Server Message Block (SMB) shares. [Performance] Fixed: This update addresses an issue that prevented the complete removal of unused language packs and Feature on Demand packages, which previously led to unnecessary storage use and longer Windows Update installation times. [Shell] Fixed: This update resolves an issue where kiosk devices might stop responding after being locked and unlocked by an administrator. [Windows Hello] Fixed: This update fixes an issue that prevented the automatic renewal of expiring certificates in Windows Hello for Business. [Windows Search] Fixed: Windows Search responds very slowly—the Search Box can take over 10 seconds to load before you can use it. You can find the announcement post here.
    • Father's Day is coming, so give your dad some great gifts by Steven Parker Mashup from Depositphotos.com (1) (2) Father's Day is quickly approaching on Sunday, June 15. If you haven't gotten your dad a gift for the occasion, don't sweat it. There are lots of affordable gifts you can buy for Father's Day on Amazon, and if you order one or more of them right now, you can get them shipped to you in time to give them to your dad. Below we have put together some Apple deals, and we'll keep expanding the list as we come across more interesting deals, so be sure to check back. iPad Deals Apple iPad 11" 128GB A16 Tablet $299 -14% now $299 (was $349) Apple iPad Air 11" 128GB M3 Chip Tablet -17% now $499 (was $559) Apple iPad Air 13" 128GB M3 Tablet -12% now $699 (was $799) 2024 iPad Mini A17 Pro 128GB 8.3" Tablet -20% now $399 (was $499) 2024 iPad Pro 11" 256GB M4 OLED Tablet -10% now $899 (was $999) 2024 iPad Pro 13" 256GB M4 OLED Tablet -15% $1099 (was $1299) Apple Pencil (3rd Gen, For Select iPads) -13% now $69 (was $79) Apple Pencil Pro (For Select iPad Pro & Air) -23% now $99 (was $129) AirPods deals Apple AirPods Pro 2 Wireless Earbuds -32% now $169 (was $249) Apple AirPods 4 Spatial Audio Wireless Earbuds -23% now $99 (was $129) Apple AirPods 4 Active Noise Canceling Wireless Earbuds -17% now $149 (was $179) Apple Watch Deals Series 10 GPS 42mm (Sport Band) -25% now $299 (was $399) Series 10 GPS 42mm (Sport Loop) -25% now $299 (was $429) Series 10 GPS 42mm (Sport Loop) -23% now $329 (was $429) Series 10 GPS 46mm (Sport Band) -23% now $329 (was $429) Apple Watch Ultra 2 49mm GPS Smartwatch -8% from $739 (reg $799) Apple Watch SE (2nd Gen) Smartwatch -32% from $169 (was $249) MacBook Deals 2025 MacBook Air 13.6" M4 Chip Laptop (16GB/256GB) -15% now $849 (was $999) 2025 MacBook Air 15.3" M4 Chip Laptop (16GB/256GB) -13% now $1049 (was $1199) 2024 MacBook Pro M4 14.2" Laptops -11% from $1429 (was $1599) 2024 MacBook Pro M4 16" Laptops -10% from $2249 (was $2499) Mac Deals Mac Mini M4 10-Core CPU 10-Core GPU -8% now $546 (was $599) iMac M4 24" 8-Core CPU/GPU (16GB/256GB) -8% now $1193 (was $1299) iMac M4 24" 10-Core CPU/GPU (16GB/256GB) -7% now $1349 (was $1499) Kindle deals 16GB Kindle Scribe + Premium Pen -25% now $299.99 (was $399.99) 32GB Kindle Scribe + Premium Pen -24% now $320 (was $420) 64GB Kindle Scribe + Premium Pen -22% now $350 (was $450) Samsung 49" Odyssey OLED G9 (G95SC) 240Hz Curved Gaming Monitor -$800 now $999.99 (was $1799) Samsung Galaxy Buds FE True Wireless Bluetooth Earbuds -35% now $64.99 (was $99.99) Samsung Galaxy Tab S10+ -$120 now $879.99 (was $999.99) Samsung Galaxy Watch Ultra 47mm -31% now $449.99 (was $649.99) SAMSUNG Galaxy S25+ -12% now $879.99 (was $999.99) These are just a small selection of the discounts on offer; for more great deals, go to Amazon's Deals page. As an Amazon Associate, we earn from qualifying purchases.
  • Recent Achievements

    • One Month Later
      Orpheus13 earned a badge
      One Month Later
    • Week One Done
      Orpheus13 earned a badge
      Week One Done
    • One Year In
      Orpheus13 earned a badge
      One Year In
    • Week One Done
      serfegyed earned a badge
      Week One Done
    • Week One Done
      fashionuae earned a badge
      Week One Done
  • Popular Contributors

    1. 1
      +primortal
      519
    2. 2
      ATLien_0
      261
    3. 3
      +FloatingFatMan
      202
    4. 4
      +Edouard
      168
    5. 5
      Xenon
      121
  • Tell a friend

    Love Neowin? Tell a friend!