Containers
A container is a class or structure you create that will be serialized in place of another type (structure or class) that would not serialize or would be problematic (not efficient, missing fields, etc.).
It inherits the interface ITypeContainer
.
Making a ITypeContainer
The objective is to replace the problematic instance by an easy instance. In general, the container will contain very simple types (string
, int
, byte
array, etc.).
Let's take an example:
/// <summary> /// . There is no default (no-param) constructor. /// . The only constructor has a parameter with no corresponding field. /// . ATextBox has a private 'set' and is different type from constructor's parameter. /// </summary> public class MyStrangeClassNeedsACustomContainer { /// <summary> /// It is built from the constructor's parameter. /// Since its 'set' method is not public, it will not be serialized directly. /// </summary> public TextBox ATextBox { get; private set; } public MyStrangeClassNeedsACustomContainer(int NumberAsTitle) { this.ATextBox = new TextBox() { Text = NumberAsTitle.ToString() }; } }
As written in the summary, this class causes some difficulties to the serializer(s).
To overcome the problem, we create a container:
class ContainerForMyStrangeClass : ITypeContainer { #region Here you add data to be serialized in place of the class instance public int AnInteger; // We store the smallest, sufficient and necessary data. #endregion Here you add data to be serialized in place of the class instance public ITypeContainer CreateNewContainer(object ContainedObject) { MyStrangeClassNeedsACustomContainer sourceInstance = ContainedObject as MyStrangeClassNeedsACustomContainer; return new ContainerForMyStrangeClass() { AnInteger = int.Parse(sourceInstance.ATextBox.Text) }; } public object Deserialize() { return new MyStrangeClassNeedsACustomContainer(this.AnInteger); } public bool IsValidType(Type type) { return Tools.TypeIs(type, typeof(MyStrangeClassNeedsACustomContainer)); } public bool ApplyEvenIfThereIsAValidConstructor { get { return false; } } public bool ApplyToStructures { get { return false; } } }
A detail: all methods behave as static methods (although they are instance methods).
Except Deserialize()
.
Please note a container class can have two roles:
- As a container constructor.
This role comes from the interfaceIContainerGenerator
.
One, and only one, instance of the container is instantiated for this role.
The serializer asks this instance if this type of container can encapsulate a value, by calling functions asIsValidType(Type type)
.
If the answer is yes, the serializer commands the container to produce another instance that will contain the value. - As an actual container.
This role comes from the interfaceIContainer
.
Every instance of this container contains a representation if the original value.
The deserializer
ITypeContainer inherits both the two interfaces (and consequently the two roles too) :public interface ITypeContainer : IContainerGenerator, IContainer { }
Let's see them more in details:
-
public int AnInteger
It is not part of theITypeContainer
interface. In this example here we store the information we need later at deserialization. -
ITypeContainer CreateNewContainer(object ContainedObject)
Called on serialization. This is a kind of constructor for this container instance. The parameter will be the source class instance to serialize. -
object Deserialize()
Called on deserialization. The container instance will produce a new instance, a copy of the source instance, using our field
AnInteger
. -
bool IsValidType(Type type)
Called on serialization. Returns true is the type can be contained in this container. This is a filter. We can choose to accept inherited types or not, to accept several compatible types, etc.. -
bool ApplyEvenIfThereIsAValidConstructor
Called on serialization. Returns true if this container applies to class types with a default (no-param) constructor. Can be useful to very general containers. -
bool ApplyToStructures
Called on serialization. Returns true if this container applies to structure types, and not only class types. Can be useful to very general containers.
Steps are:
- The serializer checks if the source type (
MyStrangeClassNeedsACustomerContainer
) is managed by a container. Our container class (ContainerForMyStrangeClass
) answers yes, viaIsValidType()
,ApplyEvenIfThereIsANoParamConstructor
andApplyToStructures
. - The serializer builds an instance of our container, via
CreateNewContainer()
.CreateNewContainer
builds an instance and sets its fieldAnInteger
. - The serializer stores (serializes) this container instance in place of the source instance.
- The deserializer retrieves (deserializes) the container instance.
- The deserializer calls
Deserialize()
and obtains a copy of the source class instance.Deserialize()
creates this copy using its fieldAnInteger
.
We declare the CustomModifiers globally :
public class CustomContainerTestModifiers : CustomModifiers { public CustomContainerTestModifiers() : base(Containers: new ITypeContainer[] { new ContainerForMyStrangeClass() // The constructor role instance. }) { } }
This declaration will automatically be found by the deserializer.
Now we serialize it:
/* This example needs a custom ITypeContainer. Normally, this class can not be serialized (see details in its source). But thanks to this container, we can serialize the class as a small data (an integer). */ var data = new MyStrangeClassNeedsACustomContainer(123); using (MemoryStream ms = new MemoryStream()) { UniversalSerializer ser = new UniversalSerializer(ms); ser.Serialize(data); var data2 = ser.Deserialize<MyStrangeClassNeedsACustomContainer>(); bool ok = data2.ATextBox.Text == "123"; }
The right container is automatically found by UniversalSerializer, thanks to the global declaration.
As you can see, the implementation is very easy.
Tool help functions
The static class Tools
offers some help:
-
Type Tools.TypeIs(Type ObjectType, Type SearchedType)
It is equivalent to the C#'s 'is
', but for Types.
For example,TypeIs((typeof(List<int>), typeof(List<>))
returnstrue
. -
Type Tools.DerivedType(Type ObjectType, Type SearchedType)
Returns the type corresponding to
SearchedType
that is inherited byObjectType
.
For example,DerivedType(typeof(MyList), typeof(List<>))
returnstypeof(List<int>)
whenMyList
isMyList: List<int> { }
-
FieldInfo Tools.FieldInfoFromName(Type t, string name)
Returns theFieldInfo
of the named field of the type.
Please share your containers
If you wrote a container for a common type, please share it.
I will be more than happy to include it in the serializer.