try {
watcher.Path = @"C:MyFolder";
} catch(ArgumentException ex) {
Console.WriteLine(ex.Message);
return;
}
// Установка фильтров наблюдения.
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
// Наблюдение только за текстовыми файлами.
watcher.Filter = "*.txt";
// Добавление обработчиков событий.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
// Начало наблюдения за каталогом.
watcher.EnableRaisingEvents = true;
// Ожидание сигнала пользователя для выхода из программы.
Console.WriteLine(@"Нажмите 'q' для выхода из приложения.");
while(Console.Read() != 'q');
}
Следующие два обработчика событий просто выводят информацию о модификации текущего файла.
static void OnChanged(object source, FileSystemEventArgs e) {
// Уведомление об изменении, создании или удалении файла.
Console.WriteLine("Файл {0} {1}!", e.FullPath, e.ChangeType);
}
static void OnRenamed(object source, RenamedEventArgs e) {
// Уведомление о переименовании файла.
Console.WriteLine("Файл {0} переименован вn{1}",
e.OldFullPath, e.FullPath);
}
Чтобы проверить работу этой программы, запустите приложение и откройте Проводник Windows. Попытайтесь переименовать, создать, удалить файлы *.txt в MyFolder или выполнить с ними какие-то другие действия, вы увидите, что консольное приложение реагирует на эти действия выводом различной информации о состоянии текстовых файлов (рис. 16.10).
Исходный код. Проект MyDirectoryWatcher размещен в подкаталоге, соответствующем главе 16.
Рис. 16.10. Наблюдение за текстовыми файлами
Асинхронный файловый ввод-вывод
В завершение нашего обзора пространства имен System.IO давайте выясним, как осуществляется асинхронное взаимодействие с типами FileStream. Один из вариантов поддержки асинхронного взаимодействия в .NET вы уже видели при рассмотрении многопоточных приложений (см. главу 14). Ввиду того, что ввод-вывод может занимать много времени, все типы, производные от System.IO.Stream, наследуют множество методов, разрешающих асинхронную обработку данных. Как и следует ожидать, эти методы работают в связке с типом IAsyncResult.
public abstract class System.IO.Stream: MarshalByRefObject, IDisposable {
…
public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state);
public virtual IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state);
public virtual int EndRead(IAsyncResult asyncResult); public virtual void EndWrite(IAsyncResult asyncResult);
}
Работа с асинхронными возможностями типов, производных от System. IO.Stream, аналогична работе с асинхронными делегатами и асинхронными удаленными вызовами методов. Маловероятно, что асинхронный подход может существенно улучшить доступ к файлам, но есть большая вероятность того, что от асинхронной обработки получат выгоду другие потоки (например, использующие сокеты). Так или иначе, следующий пример иллюстрирует подход, в рамках которого вы можете асинхронно взаимодействовать с типом FileStream.
class Program {
static void Main(string[] args) {
Console.WriteLine("Старт первичного потока, ThreadID = {0}", Thread.CurrentThread.GetHashCode());
// Следует использовать этот конструктор, чтобы получить
// FileStream с асинхронным доступом для чтения и записи.
FileStream fs = new FileStream('logfile.txt", FileMode.Append, FileAccess.Write, FileShare.None, 4096, true);
string msg = "это проверка";
byte[] buffer = Encoding.ASCII.GetBytes(msg);
// Начало асинхронной записи.
// По окончании вызывается WriteDone.
// Объект FileStream передается методу обратного вызова,
// как информация состояния.
fs.BeginWrite(buffer, 0, buffer.Length, new AsyncCallback(WriteDone), fs);
}
private static void WriteDone(IAsyncResult ar) {
Console.WriteLine("Метод AsyncCallback для ThreadID = {0}", Thread.CurrentThread.GetHashCode());
Stream s = (Stream)ar.AsyncState;
s.EndWrite(ar);
s.Close();
}
}
Единственным заслуживающим внимания моментом (при условии, что вы помните основные особенности использования делегатов!) в этом примере является то, что для разрешения асинхронного поведения типа FileStream вы должны использовать специальный конструктор (который здесь и используется). Последний параметр System.Boolean (если он равен true) информирует объект FileStream о том, что соответствующие операции должны выполняться во вторичном потоке.
Исходный код. Проект AsynсFileStream размещен в подкаталоге, соответствующем главе 16.
Резюме
Эта глава начинается с рассмотрения типов Directory(Info) и File(Info) (а также нескольких новых членов типа File, появившихся в .NET 2.0). Вы узнали о том, что эти классы позволяют работать с физическими файлами или каталогами на жестком диске. Затем был рассмотрен ряд типов (в частности, FileStream), полученных из абстрактного класса Stream. Поскольку типы, производные от Stream, работают с потоком "сырых" байтов, пространство имен System.IO предлагает множество типов ввода-вывода (StreamWriter, StringWriter, BinaryWriter и т.п.), упрощающих процесс.
В процессе обсуждения был также рассмотрен новый тип .NET 2.0 DriveType, вы узнали о том, как контролировать файлы с помощью типа FileSystemWatcher и как взаимодействовать с потоками в асинхронном режиме.
ГЛАВА 17. Сериализация объектов
Из главы 16 вы узнали о функциональных возможностях, предоставленных пространством имея System.IO. Было показано, что это пространство имен содержит множество типов ввода-вывода, которые могут использоваться для чтения и сохранения данные в соответствий с заданными параметрами размещения (иди заданным форматом). В этой главе будет рассмотрена родственная тема сериализации объектов. С помощью объекта сериализации можно сохранять и восстанавливать состояние объекта в любом производном от System.IO.Stream типе.
Вы сразу согласитесь с тем, что возможность сериализации типов играет ключевую роль при копировании объектов на удаленную машину (этот процесс будет темой обсуждения следующей главы). Однако следует также понимать, что сериализация оказывается полезной и сама по себе, и она, скорее всего, будет играть свою роль во многих ваших .NET-приложениях (как распределенных, так и обычных), В этой главе мы обсудим различные аспекты схемы сериализации .NET, включая множество новых атрибутов, появившихся с выходом .NET 2.0 и позволяющих выполнять пользовательскую настройку соответствующего процесса.
Основы сериализации объектов
Термин сериализация означает процесс переноса состояния объекта в поток, Соответствующая сохраненная последовательность данных содержит всю информацию, необходимую для реконструкции объекта, если в дальнейшем возникает необходимость в его использовании. С помощью такой технологии очень просто сохранять огромные объемы данных (в самых разных форматах). Во многих случаях сохранение данных приложения с помощью сервиса сериализации оказывается намного менее неуклюжим, чем прямое использование средств чтения/записи, предлагаемых в рамках пространства имен System.IO.
Предположим, например, что вы создали приложение с графическим интерфейсом и хотите обеспечить конечным пользователям возможность сохранить информацию об их предпочтениях. Для этого вы можете определить класс (например, с именем UserPrefs), инкапсулирующий, скажем, 20 полей данных. Если использовать тип System.IO.BinaryWriter, вам придется вручную сохранять каждое поле объекта UserPrefs. А когда вы захотите загрузить данные из соответствующего файла обратно в память, вам придется использовать System.IO.BinaryReader и (снова вручную) прочитать каждое значение, чтобы сконфигурировать новый объект UserPrefs.
Это, конечно, выполнимо, но вы можете сэкономить себе немало времени, просто указав для класса UserPrefs атрибут [Serializable]. В этом случае для сохранения полного состояния объекта достаточно будет нескольких строк программного кода.
static void Main(string[] args) {
// Предполагаем, что для UserPrefs
// указано [Serializable].
UserPrefs userData = new UserPrefs();
userData.WindowColor = "Yellow";
userData.FontSize = "50";
userData.IsPowerUser = false;
// Теперь сохраним объект в файле user.dat.
BinaryFormatter binFormat = new BinaryFormatter();
Stream fStream = new FileStream("user.dat", FileMode.Create, FileAccess.Write, FileShare.None);
binFormat.Serialize(fStream, userData); fStream.Close();