|
При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл. Делаю все пунктуально. В чем может быть проблема? |
Работа с потоками данных
Упражнение 4. Сжатие данных в потоке FileStream
В данном упражнении познакомимся с техникой чтения/записи сжатых данных в файловом потоке FileStream, хотя то же самое будет справедливо и для некоторых других рабочих потоков. Так же как и ранее, с файлом будет связываться поток FileStream, но формат данных в потоке будут обеспечивать специализированные классы сборки System.dll:
- System.IO.Compression.DeflateStream
- System.IO.Compression. GZipStream
Оба класса-надстройки реализуют эффективные алгоритмы сжатия без потерь информации.
-
Командой File/Add/New Project добавьте к решение новый проект согласно следующему снимку мастера
-
В панели Solution Explorer вызовите контекстное меню для узла App4 и выполните команду Set as StartUp Project, чтобы назначить проект стартовым -
В панели Solution Explorer выделите узел App4 и щелкните на пиктограмме Properties, чтобы открыть панель конструктора проектов -
На вкладке Application установите выпадающий список Output type в значение Console Application, включив параллельно окну формы еще и консоль для вывода информации -
В режиме Design конструктора форм поместите на форму из панели Toolbox четыре экземпляра кнопки LinkLabel, две текстовые метки Label и настройте их в соответствии с таблицей свойств
Интерфейс формы должен выглядеть так
Мы подготовили интерфейс пользователя, который больше менять не будем и его следует замкнуть.
-
В режиме Design конструктора форм выполните команду Edit/Select All, а затем - команду Format/Lock Controls, элементы интерфейса станут недоступными для редактирования в графическом режиме проектирования -
Последовательно двойным щелчком на каждом экземпляре объекта LinkLabel создайте заготовки обработчиков и их регистрацию для событий LinkClicked
Теперь осталось заполнить обработчики кодом, реализующим сжатие и декомпрессию данных при файловых операциях записи/чтения.
-
Щелкните правой кнопкой мыши в любом месте формы Form1 (или вне формы) и выполните команду View Code, чтобы вызвать редактор процедурного кода -
Заполните файл 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.Compression;
using System.IO;
namespace App4
{
public partial class Form1 : Form
{
String fileNameDeflate = "DataDeflate.dat";
String fileNameGZip = "DataGZip.dat";
String messageDeflate = "Привет всем от DeflateStream!";
String messageGZip = "Привет всем от GZipStream!";
const Int32 lenBuf = 4;
public Form1()
{
InitializeComponent();
// Настраиваем консоль
Console.ForegroundColor = ConsoleColor.White;
Console.Title = "Упражнение 4. Сжатие данных в потоке FileStream";
Console.CursorVisible = false;
}
private void saveDeflate_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
using (FileStream fs = new FileStream(fileNameDeflate, FileMode.Create))
{
// Погружаем файловый поток в оболочку сжимающего потока
DeflateStream compress = new DeflateStream(fs, CompressionMode.Compress);
// Преобразуем строковые данные в массив байт кодировки UTF-16
byte[] bytes = new UnicodeEncoding().GetBytes(messageDeflate);
// Записываем данные в файл через сжимающий поток
compress.Write(bytes, 0, bytes.Length);
// Сливаем кэш и закрываем
compress.Flush();
compress.Close();
}
}
private void loadDeflate_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (!new FileInfo(fileNameDeflate).Exists)
{
MessageBox.Show("Нет данных для отображения",
"Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
using (FileStream fs = new FileStream(fileNameDeflate, FileMode.Open, FileAccess.Read))
{
// Создаем буфер нужной длины, обычно 1024b (1Кb)
byte[] buffer = new byte[lenBuf / 2 * 2];// Округляем до четного
// Погружаем файловый поток в оболочку декомпрессионного потока
DeflateStream decompress = new DeflateStream(fs, CompressionMode.Decompress);
int count = 0;
String msg = String.Empty;
// Читаем все данные через декомпрессионный поток
while ((count = decompress.Read(buffer, 0, buffer.Length)) != 0)
msg += new UnicodeEncoding().GetString(buffer, 0, count);
// Сливаем кэш и закрываем
decompress.Flush();
decompress.Close();
// Печатаем прочитанное
Console.Clear();
Console.WriteLine(msg);
}
}
private void saveGZip_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
using (FileStream fs = new FileStream(fileNameGZip, FileMode.Create))
{
// Погружаем файловый поток в оболочку сжимающего потока
GZipStream compress = new GZipStream(fs, CompressionMode.Compress);
// Преобразуем строковые данные в массив байт кодировки UTF-16
byte[] bytes = new UnicodeEncoding().GetBytes(messageGZip);
// Записываем данные в файл через сжимающий поток
compress.Write(bytes, 0, bytes.Length);
// Сливаем кэш и закрываем
compress.Flush();
compress.Close();
}
}
private void loadGZip_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (!new FileInfo(fileNameGZip).Exists)
{
MessageBox.Show("Нет данных для отображения",
"Предупреждение", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
using (FileStream fs = new FileStream(fileNameGZip, FileMode.Open, FileAccess.Read))
{
// Создаем буфер нужной длины, обычно 1024b (1Кb)
byte[] buffer = new byte[lenBuf / 2 * 2];// Округляем до четного
// Погружаем файловый поток в оболочку декомпрессионного потока
GZipStream decompress = new GZipStream(fs, CompressionMode.Decompress);
int count = 0;
String msg = String.Empty;
// Читаем все данные через декомпрессионный поток
while ((count = decompress.Read(buffer, 0, buffer.Length)) != 0)
msg += new UnicodeEncoding().GetString(buffer, 0, count);
// Сливаем кэш и закрываем
decompress.Flush();
decompress.Close();
// Печатаем прочитанное
Console.Clear();
Console.WriteLine(msg);
}
}
}
}-
Испытайте работу приложения и разберитесь с кодом
Один из результатов будет таким
Упражнение 5. Буферизованная обертка BufferedStream
Когда создается рабочий поток, связанный с каким-либо устройством, он работает синхронно с кодом приложения и для медленных устройств приложение будет ждать завершения соответствующих операций ввода/вывода данных. Буферизация таких операций в кэше памяти, где данные могут группироваться в большие порции, может существенно ускорить работу приложения. Для этого и существует обертка BufferedStream, в которую можно погрузить любой основной поток. Тогда все манипуляции с данными будут выполняться с помощью этого вспомогательного инструмента.
Рассмотрим работу с буферизованным потоком на примере его совместного использования с файловым потоком FileStream.
-
Командой File/Add/New Project создайте новый проект App5 типа консольного приложения и назначьте его стартовым
-
Заполните файл Program.cs следующим кодом
using System;
using System.Collections.Generic;
using System.Text;
// Дополнительные пространства имен
using System.IO;
namespace App5
{
class Program
{
static void Main(string[] args)
{
// Настраиваем консоль
Console.ForegroundColor = ConsoleColor.White;
Console.Title = "Упражнение 5. Буферизованная обертка BufferedStream";
String fileName = "DataBuffered.dat";
String message = "Привет всем от BufferedStream!";
FileMode mode = FileMode.Create;
if (File.Exists(fileName))
mode = FileMode.Append;
// Пишем
using (FileStream fs = new FileStream(fileName, mode))
{
using (BufferedStream bs = new BufferedStream(fs, 1024 * 4))
{
// Применим кодировку, по умолчанию установленную в Windows
byte[] bytes = Encoding.Default.GetBytes(message);
// Дописываем побайтно в обертку
for (int i = 0; i < bytes.Length; i++)
bs.WriteByte(bytes[i]);
// Дополняем символами новой строки "\r\n"
bs.Write(Encoding.Default.
GetBytes(Environment.NewLine), 0, Environment.NewLine.Length);
/********** Вариант не для режима Append **********
// Записываем побайтно в обертку
while (bs.Position < bytes.Length)
bs.WriteByte(bytes[bs.Position]);
//************************************************/
}
}
// Читаем
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
fs.Seek(0, SeekOrigin.Begin);// Необязательно
using (BufferedStream bs = new BufferedStream(fs))// По умолчанию 4096 bytes
{
byte[] bytes = new byte[bs.Length];
int result, i = 0;
while ((result = bs.ReadByte()) != -1)
bytes[i++] = (byte)result;
/*********** Вариант ****************
while (bs.Position < bytes.Length)
bytes[bs.Position] = (byte)bs.ReadByte();
//**********************************/
// Печатаем результат
String msg = Encoding.Default.GetString(bytes);
Console.Clear();
Console.WriteLine(msg);
}
}
Console.ReadLine();// Тормозим консольное окно
}
}
}По умолчанию величина буфера в BufferedStream установлена размером 4 Kb, но в перегрузке конструктора мы вольны установить ее любой. В приведенном коде при каждом запуске информация записывается в файл в режиме добавления. Потоки мы явно не закрываем, поскольку используем инструкцию using().
-
Запустите приложение и испытайте его работу, разберитесь с кодом
Для нескольких запусков результат будет таким
Упражнение 6. Классы-обертки BinaryWriter и BinaryReader для работы с встроенными типами C#
Встроенными (элементарными, примитивными, базовыми, стандартными, простыми) типами C# называются типы, которые имеют ключевые слова в самом языке как псевдонимы ( alias ). Например, Int32 - int, Single - float, String - string, Double - double, Boolean - bool и т.д. Все они отностятся к типам значений и выведены из абстрактного класса ValueType. Такие типы являются стэковыми, т.е. в стэк функций при выполнении программы помещаются не адресные переменные, а сами данные. Они представляют собой структуры и обозначаются отдельными пиктограммами в панели Object Browser
Для чтения и записи таких данных в байтовый поток используются обертки BinaryReader и BinaryWriter пространства имен System.IO библиотечной сборки mscorlib.dll (специализированные читатель и писатель). Эти классы расщепляют базовый тип на последовательность байт и передают ее прикрепленному основному потоку для чтения/записи в связанное с потоком устройство.
Рассмотрим работу этих оберток на примере разнотипных данных, которые мы ранее уже использовали во втором упражнении. По прежнему, в качестве рабочего будем использовать файловый поток FileStream.
-
Добавьте к решению проект консольного приложения App6 и назначьте его стартовым -
Заполните файл Program.cs следующим кодом
using System;
using System.Collections.Generic;
using System.Text;
// Дополнительные пространства имен
using System.IO;// Для потоков
namespace App6
{
class Program
{
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.White;
Console.Title = "Упражнение 6. Двоичный ввод/вывод";
Console.CursorVisible = false;
new MyClass().Foo();
Console.ReadLine();// Задержка консоли
}
}
}
namespace App6
{
class MyClass
{
public void Foo()
{
// Сохраняемые данные
string sData1 = "Привет всем!";
string sData2 = "Привет студентам!";
int iData1 = (int)12345;
int iData2 = int.MaxValue;
float fData = 67F;
double dData = 89D;
// Сохраняем значения в файле
String fileName = "Data.dat";// Будет создан в каталоге сборки
// Объявляем рабочий поток
FileStream fs = null;
try
{
// Создаем или усекаем файл
fs = new FileStream(fileName, FileMode.Create);
// Подключаем к писателю
BinaryWriter data = new BinaryWriter(fs);
// Пишем данные, компилятор по типу сам
// определяет нужную перегрузку метода
data.Write(sData1);
data.Write(sData2);
data.Write(iData1);
data.Write(iData2);
data.Write(fData);
data.Write(dData);
fs.Flush(); // Сплюнуть кэш (необязательно!)
}
finally
{
if (fs != null)
fs.Close();
}
// Читаем значения из файла и печатаем их на консоль
try
{
// Открываем только для чтения
fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
if (fs.CanSeek) // Поддерживает ли такую возможность
fs.Seek(0L, SeekOrigin.Begin); // Необязательно!
// Подключаем к читателю, считываем и печатаем
BinaryReader data = new BinaryReader(fs);
// Порядок чтения должен строго соответствовать порядку записи
Console.WriteLine(data.ReadString());
Console.WriteLine(data.ReadString());
Console.WriteLine(data.ReadInt32());
Console.WriteLine(data.ReadInt32());
Console.WriteLine(data.ReadSingle());
Console.WriteLine(data.ReadDouble());
// Измеряем длину потока, пока открыт
Console.WriteLine("==============\nВсего байт: {0}", fs.Length);
}
finally
{
fs.Close();
}
// Измеряем длину файла
Console.WriteLine("Длина файла: {0}", new FileInfo(fileName).Length);
}
}
}-
Разберитесь с кодом и запустите приложение - результат будет таким
Не стоит думать, что данные при записи разбиваются объектом BinaryWriter по такой же схеме, как мы это делали во втором упражнении. Если попробовать записать с помощью этого писателя, а прочитать побайтно как в Упражнении 2, то ничего не получится: у рассмотренных читателя и писателя свои форматы данных. Хотя, расщипление на байты соответствует длине типов (кроме string ), например, тип int состоит из 4 байтов и точно такой же длинной будет записан в поток.
Очень интересно отметить, что при записи нескольких строк подряд метод в конце каждой строки ставит метку, чтобы при чтении извлекалась только одна очередная строка. Это очень удобно и позволит нам использовать заголовок библиотечного файла в следующем упражнении.
В данном упражнении мы продемонстрировали только простейшие возможности объектов BinaryReader и BinaryWriter. Они могут применяться и для реализации более сложных схем чтения/записи. Для этого имеется все необходимое: перегрузки конструкторов, методы последовательного доступа, методы сдвига указателя. Но мы их рассматривать не будем, поскольку наша основная задача - выполнить обзор потоков как можно шире, а впереди еще много вкусного.






