Текст книги "Программирование мобильных устройств на платформе .NET Compact Framework"
Автор книги: Иво Салмре
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 64 (всего у книги 69 страниц)
total_result = ""
'Выполнять цикл до максимального значения x_counter, каждый раз
'присоединяя очередную тестовую строку к рабочей строке
Dim x_counter As Integer
For x_counter = 1 To COUNT_UNTIL
total_result = total_result + numberToStore.ToString() + ", "
'Увеличить значение счетчика
numberToStore = numberToStore + 1
Next
Next
PerformanceSampling.StopSample(0)
'Отобразить длину строки
MsgBox("String Length: " + total_result.Length.ToString())
'Отобразить строку
MsgBox("String : " + total_result)
'Отобразить длительность интервала времени, ушедшего на вычисления
MsgBox(PerformanceSampling.GetSampleDurationText(0))
End Sub
'–
'ГОРАЗДО БОЛЕЕ ЭФФЕКТИВНЫЙ АЛГОРИТМ!
'Для имитации создания типичного набора строк используется
'конструктор строк (String Builder)
'–
Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
'Вызвать сборщик мусора, чтобы тест
'начинался с чистого состояния.
'ПРИБЕГАЙТЕ К ЭТОЙ МЕРЕ ТОЛЬКО В ЦЕЛЯХ ТЕСТИРОВАНИЯ! Вызовы
'сборщика мусора в программах вручную будут приводить к снижению
'общей производительности приложений!
System.GC.Collect()
Dim sb As System.Text.StringBuilder = _
New System.Text.StringBuilder
Dim total_result As String
Dim numberToStore As Integer
PerformanceSampling.StartSample(1, "StringBuilder")
Dim outer_loop As Integer
For outer_loop = 1 To LOOP_ITERATIONS
'Очистить конструктор строк
sb.Length = 0
'Очистить строку со старым результатом
total_result = ""
'Выполнять цикл до максимального значения x_counter, каждый раз
'присоединяя очередную тестовую строку к рабочей строке
Dim x_counter As Integer
For x_counter = 1 To COUNT_UNTIL
sb.Append(numberToStore)
sb.Append(", ")
'Увеличить значение счетчика
numberToStore = numberToStore + 1
Next
'Имитируем выполнение некоторых операций над строкой...
total_result = sb.ToString()
Next
PerformanceSampling StopSample(1)
'Отобразить длину строки
MsgBox("Длина строки: " + total_result.Length.ToString())
'Отобразить строку
MsgBox("String : " + total_result)
'Отобразить длительность интервала времени, ушедшего на вычисления
MsgBox(PerformanceSampling.GetSampleDurationText(1))
End Sub
Примеры к главе 9 (производительность и многопоточное выполнение)
Листинг 9.1. Код для управления выполнением одиночной задачи фоновым потоком
Option Strict On
Imports System
Public Class ThreadExecuteTask
'Перечисляем возможные состояния
Public Enum ProcessingState
'–
'Начальное состояние
'–
'Перечисляем возможные состояния
notYetStarted
'–
'Рабочие состояния
'–
'Ожидание запуска фонового потока
waitingToStartAsync
'Выполнение кода в фоновом потоке
running
'Запросить отмену выполнения вычислений
requestAbort
'–
'Состояния завершения
'–
'Состояние завершения: выполнение фонового потока
'успешно завершено
done
'Состояние завершения: выполнение потока отменено
'до его завершения
aborted
End Enum
Private m_processingState As ProcessingState
Public Delegate Sub ExecuteMeOnAnotherThread(_
ByVal checkForAborts As ThreadExecuteTask)
Private m_CallFunction As ExecuteMeOnAnotherThread
Private m_useForStateMachineLock As Object
Public Sub New(ByVal functionToCall As ExecuteMeOnAnotherThread)
'Создать объект, который мы можем использовать в конечном автомате
'в целях блокировки
m_useForStateMachineLock = New Object
'Обозначить готовность к началу выполнения
m_processingState = ProcessingState.notYetStarted
'Сохранить функцию, которую необходимо вызвать
'в новом потоке
m CallFunction = functionToCall
'–
'Создать новый поток и вызвать в нем функцию на выполнение:
' this.ThreadStartPoint()
'–
Dim threadStart As System.Threading.ThreadStart threadStart = _
New System.Threading.ThreadStart(AddressOf ThreadStartPoint)
Dim newThread As System.Threading.Thread
newThread = New System.Threading.Thread(threadStart)
'Обозначить готовность к началу выполнения (в целях определенности
'это важно сделать еще до того, как будет запущен поток!)
setProcessingState(ProcessingState.waitingToStartAsync)
'Дать ОС команду начать выполнение нового потока в асинхронном режиме
newThread.Start()
'Возвратить управление функции, вызывающей этот поток
End Sub
'–
'Эта функция является точкой входа, вызываемой для
'выполнения в новом потоке
'–
Private Sub ThreadStartPoint()
'Установить состояние обработки, соответствующее выполнению
'функции в новом потоке!
setProcessingState(ProcessingState.running)
'Запустить на выполнение пользовательский код и передать указатель в наш
'класс, чтобы этот код мог периодически проверять, не поступил ли запрос
'на прекращение выполнения
m_CallFunction (Me)
'Если выполнение не было отменено, изменить состояние таким образом,
'чтобы оно соответствовало успешному завершению
If (m_processingState <> ProcessingState.aborted) Then
'Обозначить завершение выполнения
setProcessingState(ProcessingState.done)
End If
'Выйти из потока...
End Sub
'–
'Конечный автомат
'–
Public Sub setProcessingState(ByVal nextState As _
ProcessingState)
'В любой момент времени только одному потоку выполнения могут быть
'разрешены попытки изменить состояние
SyncLock (m_useForStateMachineLock)
'В случае попытки повторного вхождения в текущее состояние
'никакие дополнительные действия не выполняются
If (m_processingState = nextState) Then
Return
End If
'–
'Простейший защитный код, гарантирующий
'невозможность перехода в другое состояние, если задача либо
'успешно завершена, либо успешно отменена
'–
If ((m_processingState = ProcessingState.aborted) OrElse _
(m_processingState = ProcessingState.done)) Then
Return
End If
'Убедиться в допустимости данного изменения состояния
Select Case (nextState)
Case ProcessingState.notYetStarted
Throw New Exception _
("Переход в состояние 'notYetStarted' невозможен")
Case ProcessingState.waitingToStartAsync
If (m_processingState <> ProcessingState.notYetStarted) Then
Throw New Exception("Недопустимое изменение состояния")
End If
Case ProcessingState.running
If (m_processingState <> _
ProcessingState.waitingToStartAsync) Then
Throw New Ехсерtion("Недопустимое изменение состояния")
End If
Case ProcessingState.done
'Мы можем завершить работу лишь тогда, когда она выполняется.
'Это возможно также в тех случаях, когда пользователь затребовал
'отмену выполнения, но работа к этому моменту уже была закончена
If ((m_processingState <> ProcessingState.running) AndAlso _
(m_processingState <> ProcessingState.requestAbort)) Then
Throw New Exception("Недопустимое изменение состояния")
End If
Case ProcessingState.aborted
If (m_processingState <> ProcessingState.requestAbort) Then
Throw New Exception("Недопустимое изменение состояния")
End If
End Select
'Разрешить изменение состояния
m_processingState = nextState
End SyncLock
End Sub
Public ReadOnly Property State() As ProcessingState
Get
Dim currentState As ProcessingState
'Предотвратить попытки одновременного чтения/записи состояния
SyncLock (m_useForStateMachineLock)
currentState = m_orocessingState
End SyncLock
Return currentState
End Get
End Property
End Class
Листинг 9.2. Тестовая программа для выполнения работы в фоновом потоке
Option Strict On
Imports System
'–
'Тестовый код, который используется для выполнения фоновым
'потоком
'–
Public Class Test1
Public m_loopX As Integer
'–
'Функция, вызываемая фоновым потоком
' [in] threadExecute: Класс, управляющий выполнением нашего потока.
' Мы можем контролировать его для проверки
' того, не следует ли прекратить вычисления
'–
Public Sub ThreadEntryPoint(ByVal threadExecute As _
ThreadExecuteTask)
'Это окно сообщений будет отображаться в контексте того потока,
'в котором выполняется задача MsgBox("Выполнение ТЕСТОВОГО ПОТОКА")
'–
' 60 раз
'–
For m_loopX = 1 To 60
'Если затребована отмена выполнения, мы должны завершить задачу
If (threadExecute.State = _
ThreadExecuteTask.ProcessingState.requestAbort) Then
threadExecute.setProcessingState( _
ThreadExecuteTask.ProcessingState.aborted)
Return
End If
'Имитировать выполнение работы: пауза 1/3 секунды
System.Threading.Thread.Sleep(333)
Next
End Sub
End Class
Листинг 9.3. Код для запуска и тестирования приведенного выше тестового кода
'Класс, который будет управлять выполнением нового потока
Private m_threadExecute As ThreadExecuteTask
'Класс, метод которого мы хотим выполнять в асинхронном режиме
Private m_testMe As Test1
'–
'Этот код должен быть запущен ранее другого кода, поскольку он запускает
'новый поток выполнения!
'
'Создать новый поток и обеспечить его выполнение
'–
Private Sub buttonStartAsyncExecution_Click(ByVal sender _
As System.Object, ByVal e As System.EventArgs) _
Handles buttonStartAsyncExecution.Click
'Создать экземпляр класса, метод которого мы хотим вызвать
'в другом потоке
m_testMe = New Test1
'Упаковать точку входа метода класса в делегат
Dim delegateCallCode As _
ThreadExecuteTask.ExecuteMeOnAnotherThread
delegateCallCode = _
New ThreadExecuteTask.ExecuteMeOnAnotherThread(AddressOf _
m_testMe.ThreadEntryPoint)
'Дать команду начать выполнение потока!
m_threadExecute = New ThreadExecuteTask(delegateCallCode)
End Sub
'Принудительно вызвать запрещенное изменение состояния (это приведет
'к возбуждению исключения)
Private Sub buttonCauseException_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles buttonCauseException.Click
m_threadExecute.setProcessingState( _
ThreadExecuteTask.ProcessingState.notYetStarted)
End Sub
'Послать асинхронному коду запрос с требованием отмены его выполнения
Private Sub buttonAbort_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles buttonAbort.Click
m_threadExecute.setProcessingState( _
ThreadExecuteTask.ProcessingState.requestAbort)
End Sub
'Проверить состояние выполнения
Private Sub buttonCheckStatus_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles ButtonCheckStatus.Click
'Запросить у класса управления потоком, в каком состоянии он находится
MsgBox(m_threadExecute.State.ToString())
'Запросить класс, метод которого выполняется в потоке,
'o состоянии выполнения
MsgBox(m_testMe.m_loopX.ToString())
End Sub
Листинг 9.4. Код, который должен быть помещен в класс Smartphone Form1.cs
'–
'Весь этот код должен находиться внутри класса Form1.cs
'–
'Объект, который будет выполнять все фоновые вычисления
Private m_findNextPrimeNumber As FindNextPrimeNumber
'–
'Обновить текст, информирующий о состоянии...
'–
Sub setCalculationStatusText(ByVal text As String)
Label1.Text = text
End Sub
Private Sub menuItemExit_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles menuItemExit.Click
Me.Close()
End Sub
'–
'Пункт меню для начала фоновых вычислений
'–
Private Sub menuItemStart Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles menuItemStart.Click
'Число, с которого мы хотим начать поиск
Dim startNumber As Long = System.Convert.ToInt64(TextBox1.Text)
'Установить фоновое выполнение
m_findNextPrimeNumber = New FindNextPrimeNumber(startNumber)
'Запустить выполнение задачи в фоновом режиме...
m_findNextPrimeNumber.findNextHighestPrime_Async()
'Установить таймер, используемый для контроля длительности вычислений
Timer1.Interval = 400 '400 мс
Timer1.Enabled = True
End Sub
'–
'Пункт меню для "отмены" выполняющейся задачи
'–
Private Sub menuItemAbortClick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles menuItemAbort.Click
'Не делать ничего, если вычисления не выполняются
If (m_findNextPrimeNumber Is Nothing) Then Return
'Установить поток в состояние прекращения выполнения
m_findNextPrimeNumber.setProcessingState( _
FindNextPrimeNumber.ProcessingState.requestAbort)
'Немедленно известить пользователя 'o готовности прекратить выполнение...
setCalculationStatusText("Ожидание прекращения выполнения...")
End Sub
'–
'Этот таймер, вызываемый потоком пользовательского интерфейса,
'позволяет отслеживать состояние выполнения 'фоновых вычислений
'–
Private Sub Timer1_Tick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Timer1.Tick
'Если к моменту вызова искомое простое число еще
'не было найдено, отключить таймер
If (m_findNextPrimeNumber Is Nothing) Then
Timer1.Enabled = False
Return
End If
'–
'Если выполнение было отменено, освободить объект,
'осуществляющий поиск, и выключить таймер
'–
If (m_findNextPrimeNumber.getProcessingState = _
FindNextPrimeNumber.ProcessingState.aborted) Then
Timer1.Enabled = False
m_findNextPrimeNumber = Nothing
setCalculationStatusText("Поиск простого числа отменен")
Return
End If
'–
'Удалось ли найти правильный ответ?
'–
If (m_findNextPrimeNumber.getProcessingState = _
FindNextPrimeNumber.ProcessingState.foundPrime) Then
Timer1.Enabled = False
'Отобразить результат
setCalculationStatusText("Число найдено! Следующее простое число = " + _
m_findNextPrimeNumber.getPrime().ToString())
m_findNextPrimeNumber = Nothing
Return
End If
'–
'Вычисления продолжаются. Информировать
'пользователя о состоянии выполнения...
'–
'Получить два выходных значения
Dim numberCalculationsToFar As Long
Dim currentItem As Long
m_findNextPrimeNumber.getExecutionProgressInfo( _
numberCalculationsToFar, currentItem)
setCalculationStatusText("Вычисления продолжаются. Поиск в области: " + _
CStr(currentItem) + ". " + _
"Для вас выполнено " + CStr(numberCalculationsToFar) + _
" расчетов!")
End Sub
Листинг 9.5. Код класса FindNextPrimeNumber.cs
Option Strict On
Imports System
Public Class FindNextPrimeNumber
'Перечисляем возможные состояния
Public Enum ProcessingState
notYetStarted
waitingToStartAsync
lookingForPrime
foundPrime
requestAbort
aborted
End Enum
Private m_startPoint As Long
Private m_NextHighestPrime As Long
'Поиск какого количества элементов выполнен?
Private m_comparisonsSoFar As Long
'Для какого элемента сейчас выполняется поиск простого числа?
Private m_CurrentNumberBeingExamined As Long
'Вызывается для обновления информации о состоянии выполнения
Public Sub getExecutionProgressInfo( _
ByRef numberCalculationsSoFar As Long, _
ByRef currentItemBeingLookedAt As Long)
'ПРИМЕЧАНИЕ. Мы используем блокирование потока для уверенности в том,
'что эти значения не считываются во время выполнения операции
'их записи. Поскольку доступ к m_comparisonsSoFar
'и m_CurrentNumberBeingExamined могут осуществлять
'одновременно несколько потоков, любая выполняемая над ними
'операция записи/считывания должна синхронизироваться с "блокировкой",
'что будет гарантировать "атомарность" этих операций
SyncLock (Me)
numberCalculationsSoFar = m_comparisonsSoFar
currentItemBeingLookedAt = m_CurrentNumberBeingExamined
End SyncLock
End Sub
Private m_processingState As ProcessingState
'–
'Простейший конечный автомат
'–
Public Sub setProcessingState(ByVal nextState As _
ProcessingState)
'Простейший защитный код, гарантирующий
'невозможность перехода в другое состояние, если задача
'либо успешно завершена, либо успешно отменена
If ((m_processingState = ProcessingState.aborted) _
OrElse (m_processingState = ProcessingState.foundPrime)) Then
Return
End If
'Разрешить изменение состояния
m_processingState = nextState
End Sub
Public ReadOnly Property getProcessingState() As ProcessingState
Get
Return m_processingState
End Get
End Property
'–
'Возвращает простое число
'–
Public Function getPrime() As Long
If (m_processingState <> ProcessingState.foundPrime) Then
Throw New Exception("Простое число еще не найдено!")
End If
Return m_NextHighestPrime
End Function
'Конструктор класса
Public Sub New(ByVal startPoint As Long)
setProcessingState(ProcessingState.notYetStarted)
m_startPoint = startPoint
End Sub
'–
'Создает новый рабочий поток, который будет вызывать функцию
'findNextHighestPrime()
'–
Public Sub findNextHighestPrime_Async()
Dim threadStart As System.Threading.ThreadStart
threadStart = _
New System.Threading.ThreadStart(AddressOf _
findNextHighestPrime)
Dim newThread As System.Threading.Thread
newThread = New System.Threading.Thread(threadStart)
'Состояние должно отвечать, что поиск продолжается
setProcessingState(ProcessingState.waitingToStartAsync)
newThread.Start()
End Sub
'–
'Основной рабочий поток. Этот поток запускает поиск очередного
'простого числа и выполняется до тех пор, пока не произойдет
'одно из следующих двух событий:
' (а) найдено очередное простое число
' (b) от внешнего (по отношению к данному) потока поступила
' команда прекратить выполнение
'–
Public Sub findNextHighestPrime()
'Если поступила команда прекратить выполнение, то поиск даже
'не должен начинаться
If (m_processingState = ProcessingState.requestAbort) Then
GoTo finished_looking
End If
'Состояние должно отвечать, что поиск продолжается
setProcessingState(ProcessingState.lookingForPrime)
Dim currentItem As Long
'Проверить, является ли число нечетным
If ((m_startPoint And 1) = 1) Then
'Число является нечетным, начать поиск со следующего нечетного числа
currentItem = m_startPoint + 2
Else
'Число является четным, начать поиск со следующего нечетного числа
currentItem = m_startPoint + 1
End If
'Приступить к поиску простого числа
While (m_processingState = ProcessingState.lookingForPrime)
'B случае нахождения простого числа, возвратить его
If (isItemPrime(currentItem) = True) Then
m_NextHighestPrime = currentItem
'Обновить состояние
setProcessingState(ProcessingState.foundPrime)
End If
currentItem = currentItem + 2
End While
finished_looking:
'Выход. К этому моменту либо от другого потока поступила
'команда прекратить поиск, либо было найдено и записано
'следующее наибольшее простое число
'Если поступил запрос прекратить выполнение,
'сообщить, что выполнение процесса прекращено
If (m_processingState = ProcessingState.requestAbort) Then
setProcessingState(ProcessingState.aborted)
End If
End Sub
'Вспомогательная функция, которая проверяет, является
'ли число простым
Private Function isItemPrime(ByVal potentialPrime _
As Long) As Boolean
'Если число – четное, значит, оно не является простым
If ((potentialPrime And 1) = 0) Then
Return False
End If
'Продолжать поиск до тех пор, пока не будет превышено значение
'квадратного корня из числа
Dim end_point_of_search As Long end_point_of_search = _
CLng(System.Math.Sqrt(potentialPrime)) + 1
Dim current_test_item As Long = 3
While (current_test_item <= end_point_of_search)
'–
'Проверить, не поступила ли команда прекратить выполнение!
'–
If (m_processingState <> ProcessingState.lookingForPrime) Then
Return False
End If
'Если число делится без остатка,
'значит, оно не является простым
If (potentialPrime Mod current_test_item = 0) Then
Return False
End If
'Увеличить число на два
current_test_item = current test_item + 2
'–
'Увеличить число проверенных элементов
'–
'ПРИМЕЧАНИЕ. Мы используем блокирование потока для уверенности в том,
'что эти значения не считываются во время выполнения операции
'их записи. Поскольку доступ к m_comparisonsSoFar
'и m_CurrentNumberBeingExamined могут осуществлять
'одновременно несколько нитей, любая выполняемая над ними
'операция записи/считывания должна синхронизироваться с "блокировкой",
'что будет гарантировать "атомарность" этих операций
SyncLock (Me)
m_CurrentNumberBeingExamined = potentialPrime
m_comparisonsSoFar = m_comparisonsSoFar + 1
End SyncLock
End While
'Число является простым
Return True
End Function
End Class
Примеры к главе 10 (производительность и XML)
Листинг 10.1. Использование XML DOM для сохранения данных в файле и их загрузки
Option Strict On
Option Compare Binary
Imports System
'–
'Демонстрирует сохранение и загрузку файлов с
'использованием объектной модели документа XML
'–
Public Class SaveAndLoadXML_UseDOM
'XML-дескрипторы, которые мы будем использовать в нашем документе
Const XML_ROOT_TAG As String = "AllMyData"
Const XML_USERINFO_TAG As String = "UserInfo"
Const XML_USERID_TAG As String = "UserID"
Const XML_NAMEINFO_TAG As String = "Name"
Const XML_FIRSTNAME _TAG As String = "FirstName"
Const XML_LASTNAME_TAG As String = "LastName"
'–
'Загружает пользовательское состояние
' [in] fileName: Имя файла, используемого для сохранения данных
' [out] userId: Загруженный идентификатор пользователя
' [out] firstName: Загруженное имя пользователя
' [out] lastName: Загруженная фамилия пользователя
'–
Public Shared Sub XML_LoadUserInfo(ByVal fileName As String, _
ByRef userId As Integer, ByRef firstName As String, _
ByRef lastName As String)
'Начинаем с нулевых значений
userId = 0
firstName = ""
lastName = ""
'Предполагаем, что данные еще не загружены
Dim gotUserInfoData As Boolean = False
Dim xmlDocument As System.Xml.XmlDocument = _
New System.Xml.XmlDocument
xmlDocument.Load(fileName)
'Получить корневой узел
Dim rootElement As System.Xml.XmlElement
rootElement = _
CType(xmlDocument.ChildNodes(0), System.Xml.XmlElement)
'Убедиться в том, что корневой узел согласуется с ожидаемым текстом,
'ибо противное означает, что мы имеем дело с каким-то другим XML-файлом
If (rootElement.Name <> XML_ROOT_TAG) Then
Throw New Exception("Тип корневого узла не совпадает с ожидаемым!")
End If
'–
'Простой конечный автомат для итеративного обхода всех узлов
'–
Dim childOf_RootNode As System.Xml.XmlElement
For Each childOf_RootNode In _
rootElement.ChildNodes
'Если это узел UserInfo, то мы хотим просмотреть его содержимое
If (childOf_RootNode.Name = XML_USERINFO_TAG) Then
gotUserInfoData = True
'Пользовательские данные найдены
'–
'Загрузить каждый из подэлементов
'–
Dim child_UserDataNode As System.Xml.XmlElement
For Each child_UserDataNode In _
childOf_RootNode.ChildNodes
'Идентификатор пользователя (UserID)
If (child_UserDataNode.Name = XML_USERID_TAG) Then
userId = CInt(child_UserDataNode.InnerText)
'ФИО пользователя (UserName)
ElseIf (child_UserDataNode.Name = XML_NAMEINFO_TAG) Then
Dim child_Name As System.Xml.XmlElement
For Each child_Name In child_UserDataNode.ChildNodes
'Имя (FirstName)
If (child_Name.Name = XML_FIRSTNAME_TAG) Then
firstName = child_Name.InnerText
'Фамилия (LastName)
ElseIf (chi1d_Name.Name = XML_LASTNAME_TAG) Then
lastName = child_Name.InnerText
End If
Next 'Конец цикла разбора UserName
End If 'Конец оператора if, осуществляющего проверку UserName
Next 'Конец цикла разбора UserInfo
End If 'Конец оператора if, осуществляющего проверку UserInfo
Next 'Конец цикла разбора корневого узла
If (gotUserInfoData = False) Then
Throw New Exception("Данные пользователя в XML-документе не найдены!")
End If
End Sub
'–
'Сохраняет пользовательское состояние
' [in] fileName: Имя файла, используемого для сохранения данных
' [in] userId: Идентификатор пользователя, который мы хотим сохранить
' [in] firstName: Имя пользователя, которое мы хотим сохранить
' [in] lastName: Фамилия пользователя, которую мы хотим сохранить
'–
Public Shared Sub XML_SaveUserInfo(ByVal fileName As String, _
ByVal userId As Integer, ByVal firstName As String, _
ByVal lastName As String)
Dim xmlDocument As System.Xml.XmlDocument = _
New System.Xml.XmlDocument
'–
'Добавить элемент документа высшего уровня
'–
Dim rootNodeForDocument As System.Xml.XmlElement
rootNodeForDocument = xmlDocument.CreateElement( _
XML_ROO T_TAG)
xmlDocument.AppendChild(rootNodeForDocument)
'–
'Добавить данные в элемент UserInfo
'–
Dim topNodeForUserData As System.Xml.XmlElement
topNodeForUserData = xmlDocument.CreateElement( _
XML_USERINFO_TAG)
rootNodeForDocument.AppendChild(topNodeForUserData)
'–
'Добавить значение UserID в наш документ
'–
'Создать подузел для информации о пространстве имен
Dim subNodeForUserID As System.Xml.XmlElement
subNodeForUserID = _
xmlDocument.CreateElement(XML_USERID_TAG)
subNodeForUserID.InnerText = _
System.Convert.ToString(userId)
'Присоединить подузел UserID к узлу высшего уровня
topNodeForUserData.AppendChild(subNodeForUserID)
'–
'Добавить все значения NameInfo в наш документ
'–
'Создать подузел для информации о пространстве имен
Dim subNodeForNameInfo As System.Xml.XmlElement
subNodeForNameInfo = xmlDocument.CreateElement( _
XML_NAMEINFO_TAG)
'Имя (FirstName)
Dim subNodeFirstName As System.Xml.XmlElement
subNodeFirstName = xmlDocument.CreateElement( _
XML_FIRSTNAME TAG)
subNodeFirstName.InnerText = firstName
'Фамилия (LastName)
Dim subNodeLastName As System.Xml.XmlElement
subNodeLastName = xmlDocument.CreateElement( _
XML_LASTNAME_TAG)
subNodeLastName.InnerText = lastName
'Присоединить подузлы имени и фамилии к родительскому узлу
'NameInfo
subNodeForNameInfo.AppendChild(subNodeFirstName)
subNodeForNameInfo.AppendChild(subNodeLastName)
'Присоединить подузел NameInfo (вместе с его дочерними узлами)
'к узлу высшего уровня
topNodeForUserData.AppendChild(subNodeForNameInfo)
'–
'Сохранить документ
'–
Try
xmlDocument.Save(fileName)
Catch ex As System.Exception
MsgBox( _
"Ошибка при сохранении XML-документа – " + ex.Message)
End Try
End Sub 'Конец функции
End Class 'Конец класса
Листинг 10.2. Вызов кода, предназначенного для сохранения и загрузки XML-документа
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Const FILENAME As String = "TestFileName.XML"
'Сохранить, используя XML DOM
SaveAndLoadXML_UseDOM.XML_SaveUserInfo(FILENAME, 14, "Ivo", _
"Salmre")
'Сохранить, используя объект однонаправленной записи XMLWriter
'SaveAndLoadXML_UseReaderWriter.XML_SaveUserInfo(FILENAME, _
' 18, "Ivo", "Salmre")
Dim userID As Integer
Dim firstName As String
Dim lastName As String
'Загрузить, используя XML DOM
SaveAndLoadXML_UseDOM.XML_LoadUserInfo(FILENAME, userID, _
firstName, lastName)
'Загрузить, используя объект однонаправленного чтения XMLReader
'SaveAndLoadXML_UseReaderWriter.XML_LoadUserInfo(FILENAME, _
' userID, firstName, lastName)
MsgBox("Готово! " + _
userID.ToString() + ", " + lastName + ", " + firstName)
End Sub
Листинг 10.3. Использование однонаправленного чтения/записи XML-данных для загрузки XML-документа из файла и его сохранения
Option Strict On
Option Compare Binary
Imports System
Public Class SaveAndLoadXML UseReaderWriter
'XML-дескрипторы, которые мы будем использовать в своем документе
Const XML_ROOT_TAG As String = "AllMyData"
Const XML_USERINFO_TAG As String = "UserInfo"
Const XML_USERID_TAG As String = "UserID"
Const XML_NAMEINFO_TAG As String = "Name"
Const XML_FIRSTNAME_TAG As String = "FirstName"
Const XML_LASTNAME TAG As String = "LastName"
'Набор состояний, отслеживаемых по мере чтения данных
Private Enum ReadLocation
inAllMyData
inUserInfo
inUserID
inName
inFirstName
inLastName
End Enum
'–
'Сохраняет пользовательское состояние
' [in] fileName: Имя файла, используемого для сохранения данных
' [in] userId: Идентификатор пользователя, который мы хотим сохранить
' [in] firstName: Имя пользователя, которое мы хотим сохранить
' [in] lastName: Фамилия пользователя, которую мы хотим сохранить
'–
Public Shared Sub XML_SaveUserInfo(ByVal fileName As String, _
ByVal userId As Integer, ByVal firstName As String, _
ByVal lastName As String)
Dim xmlTextWriter As System.Xml.XmlTextWriter
xmlTextWriter = New System.Xml.XmlTextWriter(fileName, _
System.Text.Encoding.Default)
'Записать содержимое документа!
'
xmlTextWriter.WriteStartElement(XML_ROOT_TAG)
'
xmlTextWriter.WriteStartElement(XML_USERINFO_TAG)
'
'
xmlTextWriter.WriteStartElement(XML_NAMEINFO_TAG)
'
xmlTextWriter.WriteStartElement(XML_FIRSTNAME_TAG)
'
xmlTextWriter.WriteString(firstName) 'Запись значения
xmlTextWriter.WriteEndElement() 'Закрыть дескриптор имени
'
xmlTextWriter.WriteStartElement(XML_LASTNAME_TAG)
'
xmlTextWriter.WriteString(lastName) 'Запись значения
xmlTextWriter.WriteEndElement() 'Закрыть дескриптор фамилии
'
xmlTextWriter.WriteEndElement() 'Закрыть дескриптор ФИО
'
'
xmlTextWriter.WriteStartElement(XML_USERID_TAG)
'
'Запись значения
xmlTextWriter.WriteString(userId.ToString())
xmlTextWriter.WriteEndElement() 'Закрыть дескриптор UserID
'
xmlTextWriter.WriteEndElement()
'Закрыть дескриптор UserInfo
'
xmlTextWriter.WriteEndElement() 'Закрыть дескриптор документа
xmlTextWriter.Close()
End Sub
'–
'Загружает пользовательское состояние
' [in] fileName: Имя файла, используемого для сохранения данных
' [out] userId: Загруженный идентификатор пользователя
' [out] firstName: Загруженное имя пользователя
' [out] lastName: Загруженная фамилия пользователя
'–
Public Shared Sub XML_LoadUserInfo(ByVal fileName As String, _
ByRef userId As Integer, ByRef firstName As String, _
ByRef lastName As String)
Dim currentReadLocation As ReadLocation
'Начинаем с нулевых значении
userId = 0
firstName = ""
lastName = ""
Dim xmlReader As System.Xml.XmlTextReader = _
New System.Xml.XmlTextReader(fileName)
xmlReader.WhitespaceHandling = _
System.Xml.WhitespaceHandling.None
Dim readSuccess As Boolean
readSuccess = xmlReader.Read()
If (readSuccess = False) Then
Throw New System.Exception("Отсутствуют XML-данные для чтения!")
End If
'Убедиться в том, что мы распознали корневой дескриптор
If (xmlReader.Name <> XML_ROOT_TAG) Then
Throw New System.Exception( _
"Корневой дескриптор отличается от ожидаемого!")
End If
'Отметить текущее местоположение в документе
currentReadLocation = ReadLocation.inAllMyData
'–
'Цикл прохождения документа и чтение необходимых данных
'–
While (readSuccess)
Select Case (xmlReader.NodeType)
'Вызывается при входе в новый элемент







