• 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 certainly hope you're right, but I put nothing past Microsoft these days. Writing Microsoft with a dollars sign used to be a joke because they charged for their software, but now they charge for their software "and" shove ads and recommended products down your throat at every turn, revert settings changes after updates, etc., so perhaps it's time to start spelling it Micro$oft again, :-p
    • Glow 25.08 by Razvan Serea Glow provides detailed reporting on every hardware component in your computer, saving you valuable time typically spent searching for CPU, motherboard, RAM, graphics card, and other stats. With Glow, all the information is conveniently presented in one clean interface, allowing you to easily access and review the comprehensive hardware details of your system. Glow provides detailed information on various system aspects, including OS, motherboard, processor, memory, graphics card, storage, network, battery, drivers, and services. The well-organized format ensures easy access to the required information. You can export all the gathered data to a plain text file, facilitating sharing with others for troubleshooting purposes. No installation needed. Just decompress the archive, launch the executable, and access computer-related information. Glow runs on Windows 11 and Windows 10 64-bit versions. Glow 25.08 release notes: What's new Glow's render engine has been improved. The program now supports high-resolution displays even on multi-monitor setups and monitors with varying DPI levels. It delivers sharp and clear visuals on 8K and higher DPI screens. The TSImageRenderer algorithm has been integrated into Glow. All visual icons in the interface are now automatically resized in a DPI-aware manner, ensuring high-resolution display quality. We know that Glow's Installed Drivers and Installed Services sections load slowly. That's why the loading algorithms have been reprogrammed into a parallel structure. Now it loads with up to 95% speed increase compared to the processor core. Glow's monitor testing tools have been reprogrammed. The Dead Pixel Test and Dynamic Color Range Test now function with improved accuracy. The Screen Overlay Tool has been redesigned, featuring theme sensitivity and new functions such as a close button. The startup engine for all Glow tools has been redeveloped, allowing for more efficient and effective management of the tools. The search engine's clear button in the "Installed Drivers", "Installed Services" and "Installed Applications" sections has been refreshed with a DPI-aware design for enhanced visibility. Icons have been added to the BIOS Update, Battery Report Generation, and Export buttons. Icons have been added to all buttons across Glow's tools. The Tab key functionality in Glow's interface has been improved, enabling more precise and stable navigation between elements. Glow's logo has been updated with a new design, offering a more elegant and modern appearance. Glow's primary colors have been redesigned within the Adobe RGB Color Space, giving the interface a more contemporary look. The About section has been reprogrammed. All social media buttons now feature icons, and the close button is DPI-aware and more prominent. Fixed Bugs Fixed an issue causing control buttons to overlap and shift position at high DPI settings. Resolved a DPI-related issue where checkmarks in the top menu distorted visually at high DPI values. Fixed a parallel processing error that caused the program to crash after clicking and closing information text in any monitor test tool. Corrected a bug in the Dynamic Color Range Tool that caused white space to appear on the right and bottom when resizing. Fixed calculation errors affecting the color scale and ratios in the Dynamic Color Range Tool. Resolved a layering issue that sometimes caused message boxes to appear behind the program window. Changes The backend code structure of Glow has been improved to a modular architecture, ensuring full compatibility and easier integration with other Türkay Software products. Tools have been moved back to the top menu. Some interface icons have been replaced to provide better visual clarity. A YouTube link has been added to the About section. Note: Always unzip the program before using it. Otherwise you may get an error. Download: Glow 25.08 | 3.1 MB (Open Source) View: Glow Homepage | Screenshot Get alerted to all of our Software updates on Twitter at @NeowinSoftware
    • Vivaldi 7.5.3735.56 by Razvan Serea Vivaldi is a cross-platform web browser built for – and with – the web. A browser based on the Blink engine (same in Chrome and Chromium) that is fast, but also a browser that is rich in functionality, highly flexible and puts the user first. A browser that is made for you. Vivaldi is produced with love by a founding team of browser pioneers, including former CEO Jon Stephenson von Tetzchner, who co-founded and led Opera Software. Vivaldi’s interface is very customizable. Vivaldi combines simplicity and fashion to create a basic, highly customizable interface that provides everything a internet user could need. The browser allows users to customize the appearance of UI elements such as background color, overall theme, address bar and tab positioning, and start pages. Vivaldi features the ability to "stack" and "tile" tabs, annotate web pages, add notes to bookmarks and much more. Vivaldi 7.5.3735.56 changelog: [Chromium] Update to 138.0.7204.173 Download: Vivaldi 64-bit | 123.0 MB (Freeware) Download: Vivaldi 32-bit | ARM64 View: Vivaldi Home Page | Screenshot | Release Notes Get alerted to all of our Software updates on Twitter at @NeowinSoftware
    • Floorp 11.29.0 by Razvan Serea Floorp is a cutting-edge web browser that combines the trusted foundation of Mozilla's Firefox with a unique Japanese perspective, offering users an exceptional online experience. This open-source browser prioritizes privacy, customization, and security. Floorp is transparent, with no user tracking or data sharing, and it's completely open source. With a strict no-tracking policy and full transparency, your personal information remains private. As an open-source project, Floorp not only shares its source code but also its build environment, inviting users to contribute and build their unique versions. The regular updates, based on Firefox ESR, ensure that you always have the latest features and security enhancements. Get to the point with Floorp Lightning's minimalism With a keen eye on user preferences, Floorp is gearing up to launch "Floorp Lightning," a streamlined and performance-focused browser, harkening back to the fundamentals of web browsing. This browser has undergone a meticulous transformation, shedding more than 80% of the features that characterized its predecessor. What remains are only the high-demand functionalities within the Floorp ecosystem. The result is a sleek, lean, and swift web browser optimized for maximum efficiency. In the ever-accelerating digital world, "Floorp Lightning" is poised to offer users a refreshingly nimble and responsive browsing experience, set to debut in beta mode this November. Floorp key features: Strong Tracking Protection: Floorp offers robust tracking protection, safeguarding users from malicious tracking and fingerprinting on the web. Flexible Layout: Customize Floorp's layout to your heart's content, including moving the tab bar, hiding the title bar, and more for a personalized browsing experience. Switchable Design: Choose from five distinct designs for the Floorp interface, and even switch between OS-specific designs for a unique look Regular Updates: Based on Firefox ESR, Floorp receives updates every four weeks, ensuring up-to-date security even before Firefox's releases. No User Tracking: Floorp prioritizes user privacy by abstaining from collecting personal information, tracking users, or selling user data, with no affiliations with advertising companies. Completely Open Source: The full source code for Floorp is open to the public, allowing transparency and enabling anyone to explore and build their own version. Dual Sidebar: Floorp features a versatile built-in sidebar for webpanels and browsing tools, making it perfect for multitasking and quick access to bookmarks, history, and websites. Flexible Toolbar & Tab Bar: Customize your browser with Tree Style Tabs, vertical tabs, and bookmark bar modifications, catering to both beginners and experts in customization. User-Centric Web Experience: Floorp prioritizes user privacy and collaboratively blocks harmful trackers. Floorp 11.29.0 changelog: Security fixes Download: Floorp 64-bit | 85.2 MB (Open Source) Links: Floorp Website | Github Website | Screenshot Get alerted to all of our Software updates on Twitter at @NeowinSoftware
  • Recent Achievements

    • Collaborator
      fernan99 earned a badge
      Collaborator
    • Collaborator
      MikeK13 earned a badge
      Collaborator
    • One Month Later
      Alexander 001 earned a badge
      One Month Later
    • One Month Later
      Antonio Barboza earned a badge
      One Month Later
    • Week One Done
      Antonio Barboza earned a badge
      Week One Done
  • Popular Contributors

    1. 1
      +primortal
      592
    2. 2
      ATLien_0
      225
    3. 3
      Michael Scrip
      167
    4. 4
      Xenon
      140
    5. 5
      +FloatingFatMan
      128
  • Tell a friend

    Love Neowin? Tell a friend!