Object serialization in .NET
Piet ObermeyERMICROSOFT CORPORATION Summary 2001 Abstract: Why use serialization? The most important reason is: Save the status of the object in the storage medium so that you can recreate the exactly the same copy later; values will be sent from an application domain to another. For example, serialization can be used to save session status in ASP.NET, and copy objects to a clipboard of a Windows Form. It can also be used to transmit objects remotely from one application domain to another. This paper briefly introduces serialization used in Microsoft .NET. Directory Introduction Persistent Storage Press Value Feed Basic Serial Selective Serialized Custom Series Detection Procedure Procedures Control Serialization Rules Introduction Serialization refers to the process of storing the status of the object instance to the storage medium. In this process, first convert the public field of the object and the private field and the name of the class (including the assembly of the class) into a byte stream, and then write the byte stream into the data stream. When the object is then reorganized, a copy of the same identical object is created. When you implement a serialization mechanism in an object-oriented environment, some weighing must be performed between ease of use and flexibility. As long as you have enough control capabilities to this process, you can make this process to a large extent. For example, simple binary serialization does not meet the needs, or for a particular reason, it is necessary to determine those fields in the class require serialization. The following sections will discuss the reliable sequence mechanism provided by the .NET framework and focusing on some important functions that allow you to customize the serialization process as needed. While lasting storage We often need to save the field values of the object to the disk and retrieve this data later. Although this work can be accomplished, this method is usually cumbersome and easily error, and it will become more complex when you need to track the hierarchy of the object. It is conceivable to write a case where a large number of large-scale business applications containing a large number of objects has to write code for each object to save fields and properties to disk and restore these fields and properties from disks. Serialization provides a shortcut way to easily implement this goal. Public Language Runtime (CLR) Management Objects In memory, .NET framework provides an automatic serialization mechanism by using reflection. Once the object serialization, all data members of the class, assembly, and class instance are written to the storage medium. Objects typically use a member variable to store references to other instances. After class sequence, the serialization engine will track all serialized reference objects to ensure that the same object is not serialized multiple times. The serialization architecture provided by the .NET framework can automatically handle object charts and cyclic references. The only requirement for the object chart is that all objects referenced by the sequential object must be marked as Serializable (see Basic Serialization). Otherwise, it will appear when the serialization program is attempts to serialize unmarked objects. When the serialized class is sequenceful, the class will be recreated and the value of all data members is automatically restored. Press value-enabled objects only valid in the application domain of the object. Unless the object is derived from MarshalByrefObject derived or marked as serializable, any attempt to pass object as a parameter or return it as a result will fail. If the object is marked as serializable, the object will be automatically serialized and transferred from an application domain to another, then performs reverse sequencing, thereby generating one of the objects in the second application domain. Accurate copy. This process is often referred to as a validation. If the object is derived from MarshalByrefObject, it is an object reference from a application domain to another, not an object itself. It is also possible to mark the object derived from MarshalByrefObject to Serializable.
When using this object remotely, it is responsible for serializing and pre-configured as the SurrogateRelector's formatting procedure to control the serialization process, and replaced all objects obtained from MarshalByrefObject derived with a proxy. If there is no pre-configured as a Surrogateelector, the serialization architecture will follow the following standard serialization rules (see the steps of the serialization process). Basic serialization To make a class sequentially, the easiest way is to mark it using the serializable property, as shown below: [Serializable] public class myObject {
Public INT N1 = 0;
Public INT N2 = 0;
Public string str = NULL;
} The following code snippet describes how to sequence such an instance of such an instance is a file: myObject obj = new myObject ();
Obj.n1 = 1;
Obj.n2 = 24;
Obj.str = "Some strings";
IFORMATTER FORMATTER = New BinaryFormatter ();
Stream Stream = New FileStream ("Myfile.bin", FileMode.create,
FileAccess.write, fileshare.none;
Formatter.Serialize (Stream, Obj);
Stream.close (); this example uses a binary formatting program for serialization. Simply create an instance of the stream and formatting program to use, then call the SERIALIZE method for the formatting program. The flow and the sequential object instance is supplied to this call as a parameter. All member variables in the class (even variables marked as private) will be serialized, but this is not explicitly reflected in this example. At this point, binary sequence is different from XML serialization procedures serially serialized. It is also very easy to restore the object to it. First, create a formatting program and stream for reading, and then let the formatting procedure against objects. The following code snippet shows how to do this. IFORMATTER FORMATTER = New BinaryFormatter ();
Stream Stream = New FileStream ("Myfile.bin", FileMode.Open,
FileAccess.read, Fileshare.Read;
MyObject obj = (myObject) formatter.deSerialize (fromstream);
stream.close ();
// below is certification
Console.writeLine ("N1: {0}", OBJ.N1);
Console.writeLine ("N2: {0}", OBJ.N2);
Console.writeline ("str: {0}", obj.str); the BinaryFormatter efficiency is high, which can generate a very compact byte stream. All objects that use this formatted program sequence can also use it to perform reverse sequencing, and for serialization will be reverse selecinstened on the .NET platform, this formatter is undoubtedly an ideal tool. It should be noted that the constructor is not called when the object is reversed. This constraint to reactive sequence is due to performance considerations. However, this violates some of the runtime conventions commonly used by object writers, so developers should ensure that this special agreement is taken into account when marking the object as sequentialization. If you have portability, use SOAPFORMATTER. The changes you want to do are simply convert the formatted program in the above code to SOAPFORMATTER, while Serialize and DeserialIze calls unchanged. For examples used above, the formatter will generate the following results. 1
twenty four
Some string
It should be noted that the serializable property cannot be inherited. If a new class is derived from MyObject, this new class must also mark this property, otherwise it will not be serialized. For example, if you try to serialize the following instance, a serializationException will appear, indicating that the MySTUFF type is not marked as sequential. Public class mystuff: myObject
{
Public Int N3;
} It is very convenient to use serialization, but it has some of the above restrictions. For when to mark classes for serialization (no sequence after class compile), refer to the description (see Serialization rules below). Selective serializations typically contain fields that should not be serialized. For example, suppose a class is used to store the thread ID with a member variable. When this class is reorganized, the thread corresponding to the ID stored in such a sequence may no longer run, so it is meaningless to serialize this value. They can be serialized by using the NonSerialized property tag members variables, as shown below: [Serializable]
Public class myObject
{
Public int N1;
Public INT N2;
Public String Str;
} Custom Series can be derived from the defined serialization process by implementing the iSerializable interface on an object. This feature is especially useful when the value of the member variables after the deserialization, but needs to provide a value for the variable to rebuild the full state of the object. To implement iSerializable, you need to implement the getObjectData method and a special constructor, and use this constructor when the object is retrofitted. The following code example illustrates how to implement iSerializable on the MyObject class mentioned in the previous part. [Serializable]
Public Class MyObject: iSerializable
{
Public int N1;
Public Int N2;
Public String Str;
Public myObject ()
{
}
Protected MyObject (SerializationInfo Info, StreamingContext Context)
{
N1 = Info.getInt32 ("i");
N2 = Info.get32 ("j");
Str = info.getstring ("k");
}
Public Virtual Void GetObjectData (SerializationInfo Info,
StreamingContext context) {
Info.addValue ("I", N1);
Info.addvalue ("J", N2);
Info.addvalue ("K", STR);
}
} When calling GetObjectData during the serialization, the SerializationInfo object provided in the method call is required. Simply add a variable that will be serialized in the form of name / value. Its name can be any text. As long as the serialized data is sufficient to restore the object during the reverse sequence, it is possible to freely select the member variable added to the SerializationInfo. If the base object implements iSerializable, the derived class should call the GetObjectData method for its base object. It is emphasized that when adding iSerializable to a class, you need to implement GetObjectData and special constructor simultaneously. If you lack getObjectData, the compiler will issue a warning. However, since the constructor cannot be enforced, a warning will not be issued when the constructor is missing. If you try to deactivate a certain class without constructuring, an exception will occur. The current design is better than the SetObjectData method in eliminating potential security and version control issues. For example, if the setObjectData method is defined as part of an interface, this method must be a public method, which makes the user have to write code to prevent multiple calling setObjectData methods. You can imagine that if an object is performing some operations, and a malicious application calls the SETOBJECTDATA method for this object, it will cause some potential trouble. During the reverse selecente process, the constructor provided for this purpose is passed to the class. When an object is reverse selecinstened, any visibility constraint for the constructor will be ignored, so the class is marked as public, protected, internal, or private. A nice way is to mark the constructor as protecty in the case where the class is not encapsulated. If class is packaged, it should be marked as private. To restore the status of the object, simply use the name when serialization, retrieve the value of the variable from the SerializationInfo. If the base class implements iSerializable, the constructor of the base class should be called so that the base object can restore its variables. If a new class is generated from the class that implements the iSerializable class, the constructor must be implemented simultaneously as long as the new class contains any variables that require sequentialization. The following code snippet shows how to use the MyObject class shown above to complete this. [Serializable]
Public Class ObjectTwo: MyObject
{
Public int num;
Public ObjectTWO (): base ()
{
}
Protected ObjectTwo (SerializationInfo Si, StreamingContext Context):
Base (Si, Context)
{
Num = Si.Getint32 ("NUM");
}
Public Override Void GetObjectData (SerializationInfo Si,
StreamingContext context)
{
Base.GetObjectData (Si, Context);
Si.AddValue ("Num", Num);
}
} Remember to call the base class in the reverse sequence constructor, otherwise, the constructor on the base class will never call, and the complete object cannot be constructed after rendering. Objects are thoroughly rebuilt, but the modification of the method may bring poor side effects during the reverse system, because the way to be called may reference object references that are not retrogradened in the call. If you are performing an anti-sequence-based class implementing IDSerializationCallback, the ONSERIALIZATION method will be automatically called after the entire object chart is reserved. At this time, all sub-objects referenced are completely reduced. Some classes do not use the above event listeners, it is difficult to deserialize them, and the hash table is a typical example. Retrieving keywords / values in the reverse selecente process is very easy, however, since the classes produced from the hash beta have been reversed, some problems will occur when they add these objects. Therefore, it is recommended not to call the method on the hash table. When the step of serialization is called the Serialize method on the format program, the object serialization is performed in accordance with the following rules: Check if the formatting program has an agent selection. If there is, check if the agent selection is handled by the object of the specified type. If the sequencer is processed this object type, you will call iSerializable.GetObjectData on the proxy selection. If there is no agent pickup or there is no processing, it will check if the object is tagged using the Serializable property. If not marked, the SerializationException will be triggered. If the object has been properly marked, it will check if the object is implemented. If implemented, getObjectData will be called on the object. If the object does not implement Serializable, you will use the default serialization policy to serialize all fields that are not marked nonserialized. Version control .NET Framework supports version control and side execution, and, if the interfaces are consistent, all classes can work across the versions. Since the serialization involves a member variable rather than an interface, you should be cautious when adding member variables to a class to be serialized across version, or when deleting variables. Especially for classes that do not implement iSerializable. If there is any change in the current version (such as adding member variables, changing variable types, or changing variable names), it means that if the same type of existing object is serialized using an earlier version, they cannot be successfully reversed. Serialization. If the status of the object needs to change between different versions, the author can have two options:
Implement iSerializable. This allows you to accurately control serialization and reverse sequence process, and correctly add and interpret future status during the reverse sequence. Use the Nonserialized property to tag unbelly member variables. This option is only available when it is expected to change between different versions. For example, after adding a new variable to a higher version of the class, the variable can be marked as Nonserialized to ensure that the class is kept compatible with earlier versions. Serialization rules cannot be serialized due to class compilation, so serialization should be considered when designing new categories. The problem that needs to be considered is: Do you have to send this class across an application domain? Do you want to use this class remotely? How will users use this class? Maybe they will give birth to a new class that requires serialization from my class. As long as this possibility, the class is marked as sequential. In addition to the following cases, it is best to mark all classes as serialized: