Простая
сериализация
Прежде всего
импортируйте пространство имен System.Runtime.Serialization, это сэкономит немало
времени на вводе имен. В типичной ситуации поддержка сериализации включается
простым добавлением атрибута в заголовок класса:
<Serializable()>Public
Class Employee
Атрибут <Serial
izable( )> должен быть установлен и во всех классах, производных от вашего
класса, а также во всех вложенных классах, объекты которых содержатся в исходном
классе. В противном случае рекурсивный процесс будет нарушен с исключением System.Runtime.Serialization.Serial
izationException.
В
.NET Framework сериализация поддерживается в классах, реализующих интерфейс
ISerializable.
После пометки
класса атрибутом <Serial izableC )> следует решить, в каком формате должен
сохраняться объект — в XML-формате SOAP или в более компактном двоичном формате.
Используемый по умолчанию двоичный формат доступен всегда. Чтобы воспользоваться
форматом SOAP, необходимо добавить ссылку на сборку System. Runti me. Sena1izati
on. Formatters. Soap.
Следующий
пример показывает, как организовать сериализацию для массива. Массив ArrayList
является объектом и может содержать другие объекты (в нашем примере это объекты
иерархии Employee). Поскольку динамические массивы сери-ализуются автоматически,
остается лишь пометить атрибутом <Serializabl е( )> различные классы иерархии
Empl oyee. Вся содержательная работа выполняется в двух выделенных строках:
Sub SerializeToBinary(ByVal myEmployees As ArrayList._
ByVal fName As String)
Dim fStream As
FileStream
Dim myBinaryFormatter As New Formatters.Binary.BinaryFormatter()
Try
fStream = New
FileStreamtfName,FileMode.Create, FileAccess.Write)
myBinaryFormatter.Serialize(fStream, myEmployees)
Catch e As Exception
Throw e Finally
If Not (fStream Is Nothing) Then fStream.Close()
End Try
End Sub
Чтобы вместо
двоичного формата массив сохранялся в формате SOAP, достаточно включить в проект
ссылку на сборку System.Runtime.Serialization.Formatters.Soap (это делается
в диалоговом окне Project > References) и привести выделенные строки
к следующему виду:
Dim mySoapFormatter
As New Formatters.Soap.SoapFormatter()
и
mySoapFormatter.Serialize(fStream.
myEmployees)
На рис. 9.4
показано, как выглядит полученный файл в формате SOAP.
Отдельные
поля класса можно пометить атрибутом <NonSerialized()>. Состояние этих
полей не сохраняется в процессе сериализации.
С восстановлением
сохраненного объекта дело обстоит сложнее: поскольку при десериализации возвращается
тип Object, приходится выполнять явное преобразование к нужному типу, как в
выделенной строке следующего фрагмента:
Function DeSerializeFromSoap(ByVal
fName As String) As ArrayList
Dim fStream
As New FileStreamtfName.FileMode.Open. FileAccess.Read)
Dim mySoapFormatter
As New Formatters.Soap.SoapFormatter()
Try
fStream = New
FileStream("C:\test.xml". FileMode.Open.
FileAccess.Read)
Return CType(mySoapFormatter.Deserialize(fStream), ArrayList)
Catch e As Exception
Throw e Finally
If Not (fStream Is Nothing) Then fStream.Close()
End Try
End Function
Рис.
9.4. Объект, сохраненный в формате SOAP
Применение
сериализации при клонировании объектов
У сериализации
имеется и такое нетривиальное применение, как клонирование сложных объектов.
Фокус заключается в том, чтобы записать объект в поток памяти MemoryStream и
затем восстановить его (потоки MemoryStream позволяют работать с данными в быстрой
оперативной памяти по аналогии с тем, как поток FileStream работает с файлом
на диске). Ниже приведен типичный код клониро-вания:
Public Function
Clone()As Object Implements ICloneable.Clone
Dim myBinaryFormatter As New Formatters.Binary.BinaryFormatter()
Try
Seriali'zeToBinary()
mSTream.Position = 0
Return myBinaryFormatter.Deserialize(mSTream)
Finally
mSTream.Close()
End Try
End Function
Sub SerializeToBinary()
Dim myBinaryFormatter As New Formatters.Binary.BinaryFormatter()
Try
mSTream = New MemoryStream()
myBinaryFormatter.Serialize(mSTream.Me)
Catch
Throw
End Try
End Sub
Практический
пример: динамический список с поддержкой сериализации
Прежде чем
приводить полный код примера, мы хотим предупредить об одной потенциальной трудности,
которая постоянно возникает при восстановлении сохраненных объектов, а особенно
объектов, хранящихся в динамических списках. Итак, после завершения восстановления
мы получаем набор обобщенных объектов, хранящихся в динамическом массиве.
Но как определить истинный тип этих объектов, чтобы выполнить правильное преобразование?
В следующем примере эта информация жестко фиксируется в процессе восстановления,
поскольку мы точно знаем порядок занесения объектов иерархии Employee в массив.
В более общей ситуации эти сведения пришлось бы сохранять в отдельном файле.
В настоящем
примере мы создаем менеджера (класс Manager) с именем Sally и секретаря (класс
Secretary) с именем Тот. Класс Manager содержит внутренний объект класса Secretary
в одной из переменных; класс Secretary содержит ссылку на Manager.
Не
забудьте включить в решение ссылку на сборку System.Runtime.Serialization.For-matters.Soap,
это необходимо для работы программы.
Ниже приведен
код тестовой части программы. Три ключевые строки выделены жирным шрифтом:
Option Strict
On
' Использует
сборку System.Runtime.Serialization.Formatters.Soap
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters
Module Modulel
Sub Main()
Dim Sally As
New Manager("Sally". 150000)
Dim Tom As Secretary
Tom = New Secretary("Tom".
100000, Sally)
Sally.MySecretary
= Tom
Dim Employees As New ArrayList() Employees. Add(Tom)
Employees.Add(Sally)
Console.WriteLine(Tom.TheName
& "is employee " & _
Tom.ThelD & "and has salary " & Tom.Salary)
Console.WriteLine("Tom's boss is " & Tom.MyManager.TheName)
Console.WriteLine("Sally's secretary is " & Sally.MySecretary.TheName)
Console. WriteLine()
Console.Writel_ine(Sally.TheName & "is employee " & _
Sally.ThelD & "has salary " & Sally.Salary) Sally.RaiseSalary(0.lD)
Console.WriteLinet"After
raise " & Sally.TheName &_ "has salary "_
& Sally.Salary)
Рис.9.5. Сериализация динамического массива
Ниже приведена
остальная часть кода этого примера.
Sub SerializeToSoap(ByVal myEmployees As ArrayList._
ByVal fName As String)
Dim fStream
As FileStream
Dim mySoapFormatter As New Formatters.Soap.SoapFormatter()
Try
fStream = New FileStreamtfName. FileMode.Create.FileAccess.Write)
mySoapFormatter.Serialize(fStream. myEmployees)
Catch
Throw Finally
If Not (fStream Is Nothing) Then fStream.Close()
End Try
End Sub
Function DeSerializeFromSoap(ByVal
fName As String) As ArrayList
Dim fStream
As New FileStream(fName. Fi1eMode.Open. FileAccess.Read)
Dim mySoapFormatter
As New Formatters.Soap.SoapFormatter()
Try
fStream = New FileStream(fName, FileMode.Open. FileAccess.Read)
Return
CType(mySoapFormatter.Deserialize(fStream), ArrayList)
Catch
Throw Finally
If Not (fStream Is Nothing) Then fStream.Close()
End Try
End Function
End Module
<Serializable()>Public
Class Employee
Private m_Name
As String
Private m_Salary
As Decimal
Private Const
LIMIT As Decimal = 0.1D
Private Shared
m_EmployeeId As Integer = 1000
Private m_myID
As Integer
Public Sub New(ByVal
sName As String. ByVal curSalary As Decimal)
m_Name = sName
m_Salary = curSalary
m_myID = m_EmployeeId
m_EmployeeId = m_EmployeeId + 1
End Sub
Readonly Property
TheIDO As Integer
Get
Return mjnyID
End Get
End Property
Readonly Property TheName()As String
Get
Return m_Name
End Get
End Property
Readonly Property Salary()As Decimal
Get
Return MyClass.m_Salary
End Get
End Property Public Overridable Overloads
Sub
RaiseSalary(ByVal Percent As Decimal)
If Percent > LIMIT Then
' Недопустимая операция
Console.WriteLineC'MUST
HAVE PASSWORD " & _
"TO RAISE
SALARY MORE THAN LIMIT!!!!") Else
m_Salary = (1 + Percent) * m_Salary
End If
End Sub
Public Overridable Overloads
Sub RaiseSa1ary(ByVal Percent As Decimal._
ByVal Password
As String) If Password = "special" Then
m_Salary = (1 + Percent) * m_Salary
End If
End Sub
End Class
<Serializable()>Public
Class Manager
Inherits Employee
Private m_Sec
As Secretary
Private m_Salary
As Decimal
Public Sub New(ByVal sName As String,_
ByVal curSalary
As Decimal)
MyBase.New(sName. curSalary)
End Sub
Public Sub New(ByVal
sName As String.ByVal curSalary As Decimal.
ByVal mySec As Secretary)
MyBase.New(sName.curSalary)
m_Sec = mySec
End Sub
Property MySecretary()As
Secretary Get
Return m_Sec
End Get Set(ByVal Value As Secretary)
m_Sec = Value
End Set
End Property
Public Overloads Overrides
Sub RaiseSalary(ByVal
percent As Decimal)
MyBase.RaiseSalary(2 * percent, "special")
End Sub
End Class
<Serializable()>
Public Class
Secretary Inherits Employee
Private m_Boss
As Manager
Public Sub New(ByVal
sName As String. ByVal curSalary As Decimal,
ByVal myBoss As Manager) MyBase.New(sName, curSalary)
m_Boss = myBoss
End Sub
Property MyManager()
As Manager Get
Return m_Boss
End Get Set(ByVal
Value As Manager)
m_Boss = Value
End Set
End Property
End Class