I'm trying to make a class I wrote serializable. It contains several static readonly fields that never change after construction, so they don't need to be serialized. I'm having trouble finding a way to re-set them after de-serialization that doesn't weaken the access in the base class. Here's a simplified example, starting with a non-serialisable base and derived class:
internal abstract class MyBase
{
private readonly List<int> myIntList = new List<int> ();
internal MyBase (List<int> anIntList)
{
this.myIntList = anIntList;
}
}
The derived class doesn't need to access the list, so the field can be private readonly in the base class, and the derived class set it like this:
internal sealed class MyDerived : MyBase
{
private static readonly List<int> derivedIntList = new List<int> { 1, 2, 3 };
internal MyDerived () : base (MyDerived.derivedIntList)
{
}
}
Now I want to make my derived class serializable. Since the list contents don't change, there's no need to serialize them, so I just put a DataContract attribute on both classes.
I serialize the derived class to disk like this:
private static void SeralizeDerived (string path)
{
MyDerived derived = new MyDerived ();
DataContractSerializer serializer = new DataContractSerializer (typeof (MyDerived));
using (FileStream stream = new FileStream (path, FileMode.Create))
{
serializer.WriteObject (stream, derived);
stream.Flush ();
}
}
and deserialize it like this:
private static void DeserializeDerived (string path)
{
DataContractSerializer serializer = new DataContractSerializer (typeof (MyDerived));
using (FileStream stream = new FileStream (path, FileMode.Open, FileAccess.Read))
{
MyDerived derived = serializer.ReadObject (stream) as MyDerived;
// debugger shows derived.myIntList as null, as expected
}
}
As per the comment in DeserializeDerived, the value of derived.myIntList is null. This isn't surprising to me - I didn't ask it to be serialized, and I haven't done anything to re-create it after deserialization.
The problem is this: The only way I know to fix this is to change the access to myIntList to make it protected, and have an OnDeserialized method in the MyDerived class that (re)sets myIntList:
private void ReInitialize ()
{
base.myIntList = MyDerived.derivedIntList;
}
[OnDeserialized]
private void OnDeserialized (StreamingContext context)
{
this.ReInitialize ();
}
internal MyDerived () : base ()
{
this.ReInitialize ();
}
This feels wrong - I don't want to weaken the access to the base class members, and initializing the members field-by-field in the derived constructor is more error-prone. The question is: How can I keep the same member protection but still support serialization?
Passing static list into the base class constructor looks strange for me. I would say that something is wrong with inheritance here.
If you still want to use inheritance try approach with abstract property:
public abstract class Data
{
public abstract int[] list { get;}
}
public class Data2 : Data
{
private static readonly int[] arr = new[] {1,2,3};
public override int[] list { get => arr; }
}
Related
I am trying to implement a way to save a set of objects to file, and then read it back to objects again.
I want to serialize the objects to XML (or JSON). The objects consists of one master object which holds an array of all the other objects. The array is of the type Interface, to allow several different types of child objects with some common functionality.
Obviously, there will be a problem during deserialization because the type of the interface object is not known.
Example:
[Serializable]
public class MasterClass
{
public ImyInterface[] subObjects;
}
public interface ImyInterface
{
}
How can I serialize/deserialize these objects?
My suggestions:
Add information about the object type in the serialized data.
Use a different solution than interface.
This is not the only way to serialize your data, but it is a ready to use solution from the framework:
DataContractSerializer supports this is you don't mind adding attributes for each of the available implementations of the interface:
[DataContract]
[KnownType(typeof(MyImpl))] // You'd have to do this for every implementation of ImyInterface
public class MasterClass
{
[DataMember]
public ImyInterface[] subObjects;
}
public interface ImyInterface
{
}
public class MyImpl : ImyInterface
{
...
}
Serializing/deserializing:
MasterClass mc = ...
using (var stream = new MemoryStream())
{
DataContractSerializer ser = new DataContractSerializer(typeof(MasterClass));
ser.WriteObject(stream, mc);
stream.Position = 0;
var deserialized = ser.ReadObject(stream);
}
For JSON you could use DataContractJsonSerializer instead.
One solution is to use an abstract class instead of an interface:
public class MasterClass
{
public MyAbstractClass[] subObjects;
}
[XmlInclude(typeof(MyImpl ))] //Include all classes that inherits from the abstract class
public abstract class MyAbstractClass
{
}
public class MyImpl : MyAbstractClass
{
...
}
It can be serialized/deserialized with the XmlSerializer:
MasterClass mc = ...
using (FileStream fs = File.Create("objects.xml"))
{
xs = new XmlSerializer(typeof(MasterClass));
xs.Serialize(fs, mc);
}
using (StreamReader file = new StreamReader("objects.xml"))
{
XmlSerializer reader = new XmlSerializer(typeof(MasterClass));
var deserialized = reader.Deserialize(file);
}
A member function should change the instance of the calling object. But my recent attempts didn't change the instance.
I want to provide a base class, which implements a function to deserialize a string (or file) to an object of the child class.
I tried to implement an extension method, but it doesn't work as expected. The calling instance doesn't change.
class ChildClass : BaseClass
{
[XmlAttribute(AttributeName = "attribute")]
public string Atribute { set; get; }
}
class BaseClass {}
class static BaseClassExtension
{
public static void Deserialize(this BaseClass bar, string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
bar = (BaseClass)serializer.Deserialize(reader);
}
}
}
class Program
{
public void Main()
{
ChildClass foo = new ChildClass();
foo.Deserialize("file.xml")
}
}
Within foo.Deserialize() the object "bar" is filled, but after the function ends, foo is still the "empty" new ChildClass() and does not have the content of bar.
Is it even possible to do such thing?
EDIT:
Ok lets forget the extension method. Another attempt was:
class ChildClass : BaseClass
{
[XmlAttribute(AttributeName = "atribute")]
public string Atribute { set; get; }
}
class BaseClass
{
public void Deserialize(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
XmlSerializer serializer = new XmlSerializer(this.GetType());
return (BaseClass)serializer.Deserialize(reader);
}
}
}
class Program
{
public void Main()
{
ChildClass foo = new ChildClass();
// looks pretty bad and seems quite inconvenient to me
foo = (ChildClass)foo.Deserialize("file.xml")
}
}
What I "need" is something like:
class BaseClass
{
public void Deserialize(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
XmlSerializer serializer = new XmlSerializer(this.GetType());
this = (BaseClass)serializer.Deserialize(reader);
}
}
}
Or can I use the constructor?
Best regards,
Martin
It is not possible with how this is structured. In your case, 'bar' is a local variable within the scope of the static method that, toward the end, you reassign to a different instance. You're not dealing with the same calling object.
Depending on the solution you're attempting to solve for, you could break the base/child classes apart into two separate classes, and have a member of type 'base' within 'child.'
I don't think it's possible to change the instantiated object from within itself. You can, however, put your initialization code into a static method as part of your base class, that explicitly return a new instance of the object that you can assign to a new variable. That way you are only instantiating your class once through the method.
basically you would be doing:
foo = (ChildClass)BaseClass.Deserialize("file.xml");
which would be a bit cleaner than reassigning foo from a method called from itself. Your BaseClass would then have:
public static BaseClass Deserialize(String filePath) { ... }
Previously I've had a static TopUp() method what I wanted to substitute with the Baseclass's static constructor, because it's "performed once only" as the msdn states.
msdn: Static Constuctor
Any solution for keep the derived classes' initialization in the constructor and perform it only once?
class BaseClass<T>
{
static BaseClass()
{
for (byte i = 0; i < 2; i++)
{
var temp = new Junction<byte>[2] { new Level2<byte>(), new OtherLevel2<byte>() };
Program.myList.Add(temp);
}
Console.WriteLine("static BaseClass()");
}
}
abstract class Junction<T> : BaseClass<T> { }
sealed class Level2<T> : Junction<T> { }
sealed class OtherLevel2<T> : Junction<T> { }
class Program
{
internal static List<Junction<byte>[]> myList = new List<Junction<byte>[]>();
static Program()
{
BaseClass<object> callTheStaticMethod = new BaseClass<object>();
}
static void Main()
{
Console.WriteLine("myList.Count = " + myList.Count);
Console.ReadLine();
}
}
Output:
static BaseClass()
static BaseClass()
myList.Count = 4
Your static construction is called only once. Once per type, that is.
Each time you use your BaseClass<T> with a different type parameter, that's a completely different type. So, Junction<byte>, which inherits BaseClass<byte>, is a different type from BaseClass<object>. The static constructor for BaseClass<byte> is called, as well as the static constructor for BaseClass<object>.
It's not really clear from your question what it is you're actually trying to achieve. I will say, that the use of Junction<byte> in the static constructor strongly suggests that your class is not really generic at all. Any other use of BaseClass<T> is necessarily still dependent on Junction<byte>, and thus BaseClass<byte>. And this probably negates whatever benefit you thought you were going to get by making the class generic.
You can force the static constructor to execute only once by moving it to a non-generic base class inherited by BaseClass<T>. E.g.:
class BaseClass { /* static constructor here */ }
class BaseClass<T> : BaseClass { /* other stuff */ }
But given the lack of generic-ness in the class to start with, it's not clear that this will really help much. There seems to be a broader design flaw here that should be addressed.
The issue is that you have the static initializer in a typed base class. The issue is that BaseClass<string> and BaseClass<int> are considered two different types. The actual classes are generated at compile time, and so the compiler duplicates the static initializer for each variation.
If you change the last line in the static initializer to include the name of the type you'll be able to get a better idea of this issue.
In .Net 4.6 you can do this:
Console.WriteLine($"static BaseClass<{nameof(T)}>()");
In .Net 4.0 or later you can do this:
string typeName = typeof(T).FullName;
Console.WriteLine(string.Format("static BaseClass<{0}>()", typeName));
To resolve your issue, do your static initialization in a standard class that does not have a type parameter. In this case, you can simply remove the <T> type parameter from the BaseClass. Example:
class BaseClass
{
static BaseClass()
{
for (byte i = 0; i < 2; i++)
{
var temp = new Junction<byte>[2] { new Level2<byte>(), new OtherLevel2<byte>() };
Program.myList.Add(temp);
}
Console.WriteLine($"static BaseClass<{nameof(T)}>()");
}
}
abstract class Junction<T> : BaseClass { }
sealed class Level2<T> : Junction<T> { }
sealed class OtherLevel2<T> : Junction<T> { }
Have the following structure
[Serializable]
public class Parent
{
public int x = 5;
}
[Serializable]
public class Child : Parent
{
public HashAlgorithm ha; //This is not Serializable
}
I want to serialize this using the following code:
public class Util {
static public byte[] ObjectToByteArray(Object obj)
{
if (obj == null)
{
return null;
}
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
I am working with objects of type Child in my code, however, I have a field within the Child object that is non-serializable (for example: HashAlgorithm). Hence, I attempted the convert to type Parent using the below code:
public byte[] tryToSerialize(Child c)
{
Parent p = (Parent) c;
byte[] b = Util.ObjectToByteArray(p);
return b;
}
However, this returns the error that HashAlgorithm is not serializable, despite trying to serialize the child which does not include this field. How can I accomplish what I need?
This is not possible.
You cannot serialize a class as one of its base classes.
Instead, add [NonSerialized] to the field.
You can implement ISerializable in the base class and then just pass things from derived like:
private Child() { } // Make sure you got a public/protected one in Parent
private Child(SerializationInfo info, StreamingContext context)
: base(info, context) { }
After you implement ISerializable just use the Serialize method from Child.
I am trying to create a base class where I can inherit from it (to add properties to the derived classes) and the utilized the Load and Save methods from the base class. I find myself writing the Load and Save over and over and I'd like to apply some DRY to it...
namespace Common
{
using System;
using System.IO;
using System.Xml.Serialization;
public abstract class ApplicationSettings
{
protected ApplicationSettings()
{
}
public static ApplicationSettings Load(string fileName)
{
if (!File.Exists(fileName))
{
return null;
}
XmlSerializer serializer = new XmlSerializer(typeof(ApplicationSettings));
using (StreamReader reader = new StreamReader(fileName))
{
ApplicationSettings param = (ApplicationSettings)serializer.Deserialize(reader);
reader.Close();
return param;
}
}
public void Save(string fileName)
{
XmlSerializer serializer = new XmlSerializer(typeof(ApplicationSettings));
using (StreamWriter writer = new StreamWriter(fileName))
{
serializer.Serialize(writer, this);
writer.Close();
}
}
}
}
Given this abstract class, I then want to derive a class such as:
namespace Common
{
using System;
public class ApplicationParameters : ApplicationSettings
{
public ApplicationParameters()
{
}
public string AuthorizationCode
{
get;
set;
}
public string ReferenceNumber
{
get;
set;
}
}
}
For the Derived class, I should be able to do something like
ApplicationParameters parameters =
ApplicationParmeters.Load("settings.xml");
However, in the implementation above, an compiler error occurs when I attempt to cast the ApplicationSettings to the ApplicationParameters class when I call the Load method in the base class.
Is there a way to do this?
Try replacing typeof(ApplicationSettings) with GetType().
Using this mechanism you will also tell the serializer that ApplicationParameters is a child class of ApplicationSettings. You do this via XmlInclude
[XmlInclude(typeof(ApplicationParameters))]
class ApplicationSettings
The latter is a requirements of the serializer because otherwise it won't know what class to instantiate.
Why are you using XmlSerializer ?
Unless you must control the way the output XML looks, DataContractSerializer is recommended
See here, for example
Make the top level class generic so that the Save/Load methods can support multiple types:
public abstract class ApplicationSettings<T>
{
public static T Load(string xml){ // Implementation }
public static void Save (T obj) { // Implementation }
}
public class ApplicationParameters : ApplicationSettings<ApplicationParameters>
{
}
Or you could just make the static methods themselves generic:
public abstract class ApplicationSettings
{
public static T Load<T>(string xml){ // implementation }
public static void Save<T>(T obj){ // implementation }
}
You will now notice that the Save/Load methods from the abstract parent class are strongly typed to the child so that the following line will work as expected:
ApplicationParameters parameters = ApplicationParameters.Load("settings.xml");
or
ApplicationParameters parameters =
ApplicationSettings.Load<ApplicationParameters>("settings.xml");
Depending on which method you use.
How about a constructor in your ApplicationParameters class that takes an ApplicationSettings as an argument and copy the shared properties from one to another? And then just set the not shared properties to be null or the default...