Опубликован: 05.08.2010 | Уровень: специалист | Доступ: платный
Лекция 6:

Работа с потоками данных

< Лекция 5 || Лекция 6: 12345678910111213

Упражнение 3. Сериализация данных в потоке FileStream

В предыдущем упражнении мы записывали в поток (файл) разные типы данных, представленные разным количеством байтов. И сами должны были следить за тем, чтобы записанное правильно потом считать и интерпретировать. Обычно так не делают и записывают в файл однотипные данные. Так легче ослеживать размеры данных. Но если уж пришлось чередовать в одном потоке различные данные, то лучше для этого воспользоваться сериализацией.

Сериализация - это высокоуровневая модель, встроенная в потоки .NET, которая позволяет преобразовать объект целиком в последовательность байт, сохранить его в файле, базе данных или другом источнике, а затем прочитать и полностью восстановить (десериализовать).

Для поддержки сериализации объекту необходимо следующее:

  1. Наличие атрибута Serializable перед объявлением класса (типа)
  2. Поля класса должны быть сериализуемы, что для значимых (в отличие от ссылочных!) типов всегда выполнимо
  3. Если сериализуется производный класс, то вся предшествующая цепочка наследования тоже должна состоять из сериализуемых классов

Для выполнения сериализации библиотека .NET Framework имеет три класса:

  • BinaryFormatter - сериализует объект в компактном двоичном представлении
  • SoapFormatter - сериализует объект в текстовое представление на основе XML
  • XmlSerializer - сериализует объект в текстовое представление на основе 'чистого' XML

Рассмотрим эти способы на предыдущем примере, чуток изменив сохраняемые данные.

  • Командой File/Add/New Project добавьте к решение новый проект согласно следующему снимку мастера

  • В панели Solution Explorer вызовите контекстное меню для узла App3 и выполните команду Set as StartUp Project, чтобы назначить проект стартовым
  • В панели Solution Explorer выделите узел App3 и щелкните на пиктограмме Properties, чтобы открыть панель конструктора проектов

  • На вкладке Application установите выпадающий список Output type в значение Console Application, включив параллельно окну формы еще и консоль для вывода информации

  • В режиме Design конструктора форм поместите на форму из панели Toolbox интерфейсные элементы управления и настройте их в соответствии с таблицей свойств
Таблица 19.2.
Элемент Свойство Значение
Form Text Сериализация объектов
LinkLabel (Name) saveBinary
Text Serialize BinaryFormatter and save to file
LinkLabel (Name) loadBinary
Text Load file and deserialize BinaryFormatter
LinkLabel (Name) saveSoap
Text Serialize SoapFormatter and save to file
LinkLabel (Name) loadSoap
Text Load file and deserialize SoapFormatter
LinkLabel (Name) saveXml
Text Serialize XmlSerializer and save to file
LinkLabel (Name) loadXml
Text Load file and deserialize XmlSerializer

Добавьте текстовые метки для заголовков и настройте их по своему вкусу. Интерфейс формы должен выглядеть так


Мы подготовили интерфейс пользователя, который больше менять не будем. Поэтому разумно будет его замкнуть, чтобы защитить от случайных изменений.

  • В режиме Design конструктора форм выполните команду Edit/Select All, а затем - команду Format/Lock Controls, элементы интерфейса станут недоступными для редактирования в графическом режиме проектирования
  • Последовательно двойным щелчком на каждом экземпляре объекта LinkLabel создайте заготовки обработчиков и их регистрацию для событий LinkClicked

Для тренировки мы уберем регистрацию обработчиков, созданную автоматически в файле Form1.Designer.cs, и перенесем ее в конструктор класса формы. Для этого

  • Внесите в заготовки обработчиков хотя бы комментарии, чтобы при выполнении следующего действия оболочка их не удалила
  • В режиме Design выделите на форме последовательно объекты LinkLabel, в панели Properties щелкните на пиктограмме Events и очистите поля события LinkClicked

Библиотечные классы BinaryFormatter, SoapFormatter, XmlSerializer находятся в отдельных сборках: mscorlib.dll, System.Runtime.Serialization.Formatters.Soap.dll и System.Xml.dll соответственно. Они сгруппированы в пространствах имен System.Runtime.Serialization.Formatters.Binary, System.Runtime.Serialization.Formatters.Soap и System.Xml.Serialization. Библиотека mscorlib.dll является основной и всегда подключена к проектам оболочки. Библиотека System.Xml.dll тоже подключается при создании проекта, а вот System.Runtime.Serialization.Formatters.Soap.dll нужно подключить самостоятельно.

  • В панели Solution Explorer вызовите контекстное меню для узла References проекта App3, командой Add Reference вызовите одноименное окно и подключите библиотеку System.Runtime.Serialization.Formatters.Soap.dll

  • В панели Solution Explorer вызовите контекстное меню для узла App3 и командой Add/Class добавьте к проекту новый файл с именем DataObject.cs, который заполните так
using System;
using System.Collections.Generic;
using System.Text;
    
namespace App3
{
    [Serializable()]    // Атрибут
    public class DataObject
    {
        // Сохраняемые данные
        string stringData = "Привет всем!";
        int integerData = (int)12345;       
        float floatData = 67F; 
        double doubleData = 89D;
        DateTime date;  // Инициализируем в конструкторе
    
        // Конструкторы
        public DataObject(string message)
        {
            this.stringData = message;
            this.date = DateTime.Now;
        }
        public DataObject()
        {
            this.date = DateTime.Now;
        }
    
        // Свойства доступа к полям
        public String StringData
        {
            get { return stringData; }
            set { stringData = value; }
        }
        public Int32 IntegerData
        {
            get { return integerData; }
            set { integerData = value; }
        }
        public Single FloatData
        {
            get { return floatData; }
            set { floatData = value; }
        }
        public Double DoubleData
        {
            get { return doubleData; }
            set { doubleData = value; }
        }
        public DateTime Date
        {
            get { return date; }
            set { date = value; }
        }
    }
}
  • Модифицируйте файл Form1.cs, чтобы он стал таким
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
    
// Дополнительные пространства имен
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;   // Для класса BinaryFormatter
using System.Runtime.Serialization.Formatters.Soap;     // Для класса SoapFormatter
using System.Xml.Serialization;                         // Для класса XmlSerializer
    
namespace App3
{
    public partial class Form1 : Form
    {
        String fileNameBinary = "DataBinary.dat";   // Без пути - будет создан в каталоге сборки
        String fileNameSoap = "DataSoap.dat";       // Без пути - будет создан в каталоге сборки
        String fileNameXml = "DataXml.dat";         // Без пути - будет создан в каталоге сборки
    
        public Form1()
        {
            InitializeComponent();
    
            // Настраиваем консоль
            Console.ForegroundColor = ConsoleColor.White;
            Console.Title = "Упражнение 3. Сериализация объектов";
    
            // Регистрируем обработчики вручную
            saveBinary.LinkClicked += saveBinary_LinkClicked;
            loadBinary.LinkClicked += loadBinary_LinkClicked;
            saveSoap.LinkClicked += saveSoap_LinkClicked;
            loadSoap.LinkClicked += loadSoap_LinkClicked;
            saveXml.LinkClicked += saveXml_LinkClicked;
            loadXml.LinkClicked += loadXml_LinkClicked;
        }
    
        private void saveBinary_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            FileMode mode = FileMode.Create;
            if (new FileInfo(fileNameBinary).Exists)
                mode = FileMode.Append; // Будем добавлять!
    
            using (FileStream fs = new FileStream(fileNameBinary, mode))
            {
                // Создаем объект с данными
                DataObject data = new DataObject();
                // Создаем форматировщик
                BinaryFormatter formatter = new BinaryFormatter();
                // Сериализуем объект в файл
                formatter.Serialize(fs, data);
            }
        }
    
        private void loadBinary_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (!File.Exists(fileNameBinary))
            {
                MessageBox.Show("Нет данных для отображения",
                    "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
    
            using (FileStream fs = new FileStream(fileNameBinary, FileMode.Open))
            {
                // Создать форматировщик
                BinaryFormatter formatter = new BinaryFormatter();
                // Прочитать и десериализовать все экземпляры объекта в файле
                Console.Clear();
                while (fs.Position < fs.Length)
                {
                    // Прочитать, десериализовать и переместить указатель на следующий объект
                    DataObject data = (DataObject)formatter.Deserialize(fs);
    
                    // Распечатать
                    Console.WriteLine(data.StringData);
                    Console.WriteLine(data.IntegerData);
                    Console.WriteLine(data.FloatData);
                    Console.WriteLine(data.DoubleData);
                    Console.WriteLine(data.Date.ToString());
                }
            }
        }
    
        private void saveSoap_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            FileMode mode = FileMode.Create;
            if (new FileInfo(fileNameSoap).Exists)
                mode = FileMode.Append; // Будем добавлять!
    
            using (FileStream fs = new FileStream(fileNameSoap, mode))
            {
                // Создаем объект с данными
                DataObject data = new DataObject();
                // Создаем форматировщик
                SoapFormatter formatter = new SoapFormatter();
                // Сериализуем объект в файл
                formatter.Serialize(fs, data);
            }
        }
    
        private void loadSoap_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (!File.Exists(fileNameSoap))
            {
                MessageBox.Show("Нет данных для отображения",
                    "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
    
            using (FileStream fs = new FileStream(fileNameSoap, FileMode.Open))
            {
                // Создать форматировщик
                SoapFormatter formatter = new SoapFormatter();
                // Прочитать все сериализованные объекты
                Console.Clear();
                while (fs.Position < fs.Length)
                {
                    // Десериализовать
                    DataObject data = (DataObject)formatter.Deserialize(fs);
    
                    // Распечатать
                    Console.WriteLine(data.StringData);
                    Console.WriteLine(data.IntegerData);
                    Console.WriteLine(data.FloatData);
                    Console.WriteLine(data.DoubleData);
                    Console.WriteLine(data.Date.ToString());
                }
            }
        }
    
        private void saveXml_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            // Создаем или усекаем файл 
            using (FileStream fs = new FileStream(fileNameXml, FileMode.Create))
            {
                // Создаем объект с данными
                DataObject data = new DataObject();
                // Создаем форматировщик
                XmlSerializer formatter = new XmlSerializer(typeof(DataObject));
                // Сериализуем объект в файл
                formatter.Serialize(fs, data);
            }
        }
    
        private void loadXml_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (!File.Exists(fileNameXml))
            {
                MessageBox.Show("Нет данных для отображения",
                    "Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
    
            using (FileStream fs = new FileStream(fileNameXml, FileMode.Open))
            {
                // Создать форматировщик
                XmlSerializer formatter = new XmlSerializer(typeof(DataObject));
                Console.Clear();
                // Прочитать и десериализовать
                DataObject data = (DataObject)formatter.Deserialize(fs);
    
                // Распечатать
                Console.WriteLine(data.StringData);
                Console.WriteLine(data.IntegerData);
                Console.WriteLine(data.FloatData);
                Console.WriteLine(data.DoubleData);
                Console.WriteLine(data.Date.ToString());
            }
        }
    }
}
  • Запустите приложение и испытайте все три способа - объект с данными сохраняется в файле и полностью восстанавливается

Обратите внимание на важную особенность приведенного вывода: показано время 92729 - симметричное число, хороший знак свыше ('верной дорогой идете товарищи')!

  • В панели Solution Explorer выделите узел App3, щелкните на пиктограмме Show All Files, выделите файлы DataBinary.dat, DataSoap.dat и DataXml.dat (если они созданы)

  • Командой Open контекстного меню панели Solution Explorer откройте эти файлы и ознакомтесь с форматом хранения сериализованной информации

Файловый поток FileStream все равно сам связывается с файлом, только он используется другим классом, выполняющим передачу данных в специфическом формате (сериализованных).

  • Разберитесь с кодом
< Лекция 5 || Лекция 6: 12345678910111213
Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

            // Создаем объект представления для настройки графического устройства

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?

Dmitriy Ivanchenko
Dmitriy Ivanchenko
Украина, Кировоград, Виктория-П, 2011
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974