-
Notifications
You must be signed in to change notification settings - Fork 119
Лабораториска вежба 9
Сериализација е процес на конвертирање на даден тип во состојба во која ќе може инстанци од овој тип:
- да се зачувуваат на постојана меморија како диск (во датотека, база на податоци)
- да се пренесуваат преку мрежа
- да се пренесуваат по вредност надвор од границите на системот.
Потреба од зачувување на сериализибилен објект и неговата состојба во датотека, а потоа негова десериализација и вчитување од датотека.
Ќе употребиме formatter
за да го сериализираме објектот и запишеме во System.IO.FileStream
објект. Кога имаме потреба да го вчитаме објектот, ќе го искористиме истиот тип на formatter
за да ги вчитаме сериализираните податоци од датотеката и го десериализираме објектот. Библиотеките во .NET Framework содржат класи со следните имплементации на на formatter
за сериализација на објекти во бинарен или SOAP формат:
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
-
System.Runtime.Serialization.Formatters.Soap.SoapFormatter
(Овој вид на форматер нема да го обработиме.)
Со користење на класите BinaryFormatter
и SoapFormatter
може да се сериализира инстанца од секој сериализибилен тип. Класата BinaryFormatter
продуцира бинарен податочен тек со кој се репрезентира
состојбата на објектот. SoapFormatter
класата продуцира SOAP документ. И двете BinaryFormatter
и SoapFormatter
класите го имплементираат интерфејсот System.Runtime.Serialization.IFormatter
, кој
дефинира два методи: Serialize
и Deserialize
.
Методот Serialize
прима како аргументи референца од System.IO.Stream
и референца од објектот, го сериализира објектот и го запишува во податочниот тек. Методот Deserialize
прима како аргумент рефернца од Stream
, го чита сериализираниот објект од текот и враќа како резултат референца кон десерилизираниот објект. Притоа мора да се кастира резултантната референца кон објктот од соодветиот тип.
Во следниот пример е прикажана употребата на BinaryFormatter
за да се сериализира System.Collections.ArrayList
објект кој содржи листа од стрингови (имиња на луѓе) во датотека. Потоа објектот од тип ArrayList
е десериализиран и неговата содржина е прикажана на конзолата.
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
namespace Lab9
{
class SerializacijaNaLista
{
// Serialize an ArrayList object to a binary file.
private static void BinarySerialize(ArrayList list)
{
using (FileStream str = File.Create("people.bin"))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(str, list);
}
}
// Deserialize an ArrayList object from a binary file.
private static ArrayList BinaryDeserialize()
{
ArrayList people = null;
using (FileStream str = File.OpenRead("people.bin"))
{
BinaryFormatter bf = new BinaryFormatter();
people = (ArrayList)bf.Deserialize(str);
}
return people;
}
public static void Main()
{
// Create and configure the ArrayList to serialize.
ArrayList people = new ArrayList();
people.Add("Graeme");
people.Add("Lin");
people.Add("Andy");
// Serialize the list to a file in both binary and SOAP form.
BinarySerialize(people);
// Rebuild the lists of people from the binary and SOAP
// serializations and display them to the console.
ArrayList binaryPeople = BinaryDeserialize();
Console.WriteLine("Binary people:");
foreach (string s in binaryPeople)
{
Console.WriteLine("\t" + s);
}
// Wait to continue.
Console.WriteLine("\nMain method complete. Press Enter");
Console.ReadLine();
}
}
}
Потреба да се имплементира сопствен тип кој е сериализибилен, со што се добиваат бенефити кои ги нуди сериализацијата.
За сериализација на едноставни типови, само додадете го атрибутот System.SerializableAttribute
во декларацијата на типот. За типови кои се покомплексни, или каде што треба да се контролира структурата на податоците кои што се сериализираат, се имплементира интерфејсот System.Runtime.Serialization.ISerializable
.
Претходно покажавме како се сериализира и десериализира објект со користење на форматер класите од библитеките во .NET Framework. Меѓутоа, сите типови не се сериализибилни. за да се имплементира кориснички дефиниран тип кој што ќе биде сериализибилен, мора да се додаде атрибутот SerializableAttribute
([Serializble]
) во декларацијата на типот. Се додека сите податочни полиња во овој тип се серилизбилни, се што треба да се направи е да се додаде SerializableAttribute
во декларацијата на типот. Ако се имплементира кориснички дефинирана класа која наследува од некоја друга основна класа, основната класа
исто така треба да биде сериализибилна.
Секоја formatter
класа се состои од логика потребна да ги сериализира сите типови анотирани со SerializableAttribute
и точно ќе ги сериализира сите public, protected
и private
полиња. Може да
ги исклучиме одредени полиња од процесот на сериализација со додавње на атрибутот System.NonSerializedAttribute
на соодветните полиња. Како правило, од сериализација треба да се исклучуваат следниве полиња:
- полиња кои содржат несериализибилни податочни типови
- полиња кои содржат вредности кои може да бидат неправилни во моментот кога објектот се десериализира, како што се конекции до база, адреса на меморија, ID на нитки и слични ресурси
- полиња кои содржат чуствителни или тајни информации, како што се лозинки, клучеви за енкрипција и останати лични детали за луѓе или организации
- полиња кои содржат податоци кои може лесно да се ре-креираат или извлечат од други извори, посебно кога се работи за големо количество податоци
Ако исклучите некои полиња од сериализација, мора да го имплементирате тој тип за да компензирате за сите полиња кои нема да се иницијализираат во процесот на десериализација.
За повеќето од кориснички дефинираните типови, следниот механизам е доволен за да ги исполни потребите за сериализација. Ако ви е потребна поголема контрола во процесот на сериализација, може да го имплементирате интерфејсот ISerializable
. Класите за форматирање користат различна логика кога сериализираат и десериализираат инстанци од типови кои го имплементираат ISerializable
.
За да се имплементира точно ISerializable
, треба да се направи следното:
- да се декларира декат вашиот тип го имплементира
ISerializable
- да се додаде атрибутот
SerializableAttribute
во декларацијата на типот - да се имплементира методот
ISerializable.GetObjectData
(се користи при сериализација), кој прима аргументи од типотSystem.Runtime.Serialization
,SerializationInfo
иSystem.Runtime.Serialization.StreamingContext
. - да се имплементира не јавен (
private
илиprotected
) конструктор кој прима исти аргументи како и методотGetObjectData
.
За време на процесот на сериализација, форматерот го повикува методот GetObjectData
и му пренесува референци од SerializationInfo
и StreamingContext
како аргументи. Вашиот тип мора да го пополни објектот
од типот SerializationInfo
со податоците кои сакате да ги сериализирате.
Ако креирате сериализибилна класа која наследува од основна класа која исто така имплементира ISerializable
, вашиот метод GetObjectData
и конструкторот за десериализација мора да ги повикуваат еквивалентните методи од основната класа. Класата SerializationInfo
се однесува како листа од клуч/вредност парови и содржи метод AddValue
за додавање на вакви парови. Во секој повик кон AddValue
, мора да се одреди име на парот.
Ова име се користи за време на десериализацијата за да се извлече вредноста на ова поле. Методот AddValue
има 16 преоптоварени дефиниции кои овозможуваат додавање на различни податочни типови во SerializationInfo
објектот. Објектот StreamingContext
дава информации за целта и дестинцијата на сериализираните податоци, со што ви дава можност за кои податоци да ги сериализриате. Кога форматерот десериализира инстанци од вашиот тип, го повикува конструкторот за десериализација, со што му ги пренесува референците одSerializationInfo
и StreamingContext
објектите како аргументи.
Вашиот тип мора да ги извлече сериализираните податоци од SerializationInfo
објектот со употреба на соодветните Get*
методи; на пример, со користење на GetString
, GetInt32
, или GetBoolean
. За
време на десериализацијата, објектот StreamingContext
дава информации за изворот на сериализираните податоци, со што ви дава можност да ја искористите истата логика кога сте ги сериализирале податоците.
Следниот код демонстрира сериализација на класа Employee
која го имплементира интерфејсот ISerializable
. Во овој пример, од класата Employee
нема да го сериализираме полето за адреса ако дадениот StreamingContext
објект означува дека дестинацијата на сериализацијата ќе биде во датотека.
Main
методот покажува сериализација и десериализација на објект од класата Employee
.
using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace Lab9
{
[Serializable]
public class Employee : ISerializable
{
public string Name { get; set; }
public int Age { get; set; }
public string Address;
// Simple Employee constructor.
public Employee(string name, int age, string address)
{
this.Name = name;
this.Age = age;
this.Address = address;
}
// Constructor required to enable a formatter to deserialize an
// Employee object. You should declare the constructor private or at
// least protected to ensure it is not called unnecessarily.
private Employee(SerializationInfo info, StreamingContext context)
{
// Extract the name and age of the employee, which will always be
// present in the serialized data regardless of the value of the
// StreamingContext.
Name = info.GetString("Name");
Age = info.GetInt32("Age");
// Attempt to extract the employee's address and fail gracefully
// if it is not available.
try
{
Address = info.GetString("Address");
}
catch (SerializationException)
{
Address = null;
}
}
// Declared by the ISerializable interface, the GetObjectData method
// provides the mechanism with which a formatter obtains the object
// data that it should serialize.
public void GetObjectData(SerializationInfo inf, StreamingContext con)
{
// Always serialize the employee's name and age.
inf.AddValue("Name", Name);
inf.AddValue("Age", Age);
// Don't serialize the employee's address if the StreamingContext
// indicates that the serialized data is to be written to a file.
if ((con.State & StreamingContextStates.File) == 0)
{
inf.AddValue("Address", Address);
}
}
// Override Object.ToString to return a string representation of the
// Employee state.
public override string ToString()
{
StringBuilder str = new StringBuilder();
str.AppendFormat("Name: {0}\r\n", Name);
str.AppendFormat("Age: {0}\r\n", Age);
str.AppendFormat("Address: {0}\r\n", Address);
return str.ToString();
}
}
// A class to demonstrate the use of Employee.
public class TestSerialization
{
public static void Main(string[] args)
{
// Create an Employee object representing Roger.
Employee roger = new Employee("Roger", 56, "London");
// Display Roger.
Console.WriteLine(roger);
// Serialize Roger specifying another application domain as the
// destination of the serialized data. All data including Roger's
// address is serialized.
Stream str = File.Create("roger.bin");
BinaryFormatter bf = new BinaryFormatter();
bf.Context = new
StreamingContext(StreamingContextStates.CrossAppDomain);
bf.Serialize(str, roger);
str.Close();
// Deserialize and display Roger.
str = File.OpenRead("roger.bin");
bf = new BinaryFormatter();
roger = (Employee)bf.Deserialize(str);
str.Close();
Console.WriteLine(roger);
// Serialize Roger specifying a file as the destination of the
// serialized data. In this case, Roger's address is not included
// in the serialized data.
str = File.Create("roger.bin");
bf = new BinaryFormatter();
bf.Context = new StreamingContext(StreamingContextStates.File);
bf.Serialize(str, roger);
str.Close();
// Deserialize and display Roger.
str = File.OpenRead("roger.bin");
bf = new BinaryFormatter();
roger = (Employee)bf.Deserialize(str);
str.Close();
Console.WriteLine(roger);
// Wait to continue.
Console.WriteLine(Environment.NewLine);
Console.WriteLine("Main method complete. Press Enter");
Console.ReadLine();
}
}
}
Да се имплементира апликација за исцртување на топчиња во формата со фиксна големина (радиус 25) и одредена боја која се избира од ColorPickerDialog
преку соодветно мени. Топчињата се исцртуваат со двоен лев клик во работната површина на формата.
Со помош на сериализација да се овозможи зачувување и вчитување на состојбата на програмата (исцртаните топчиња).