
- •Введение
- •Сравнение языков С++ и C#
- •Логические выражения
- •Функции для ввода и вывода в языке C#
- •Управление форматом числовых данных:
- •Обработка исключительных ситуаций
- •Методы и модификаторы параметров
- •Неявно типизированные переменные
- •Понятие класса
- •Свойства
- •Индексаторы
- •Одномерные индексаторы
- •Многомерные индексаторы
- •Перегрузка методов
- •Перегрузка знаков операций
- •Наследование
- •Виртуальные функции
- •Работа с файлами
- •Работа с каталогами
- •Абстрактный класс FileSystemInfo
- •Класс DirectoryInfo
- •Сериализация
- •FileSystemWatcher – отслеживание событий, связанных с файлами
- •Обобщения (шаблоны)
- •Интерфейсы
- •Коллекции
- •LINQ
- •Грамматика выражений запросов
- •Синтаксис запросов
- •Проекция и фильтрация
- •Упорядочение
- •Агрегирующие запросы
- •Операции с коллекциями
- •Операция Concat
- •Операция Union
- •Преобразование
- •Объединение последовательностей
- •FirstOrDefault
- •Группировка
- •Групповая адресация
- •Обработка событий
- •Групповое преобразование делегируемых методов
- •Применение методов экземпляра в качестве делегатов
- •Групповая адресация
- •Ковариантность и контравариантность
- •Класс System. Delegate
- •Назначение делегатов
- •Анонимные функции
- •Анонимные методы
- •Передача аргументов анонимному методу
- •Возврат значения из анонимного метода
- •Применение внешних переменных в анонимных методах
- •Лямбда-выражения
- •Лямбда-оператор
- •Одиночные лямбда-выражения
- •Блочные лямбда-выражения
- •События
- •Пример групповой адресации события
- •Применение аксессоров событий
- •Разнообразные возможности событий
- •Применение анонимных методов и лямбда-выражений вместе с событиями
- •Рекомендации по обработке событий в среде .NET Framework
- •Применение делегатов EventHandler<TEventArgs> и EventHandler
- •Практический пример обработки событий

Чернов Э. А. |
- 65 - |
Лекции по языку C# v 2.3 |
}
class DynShapes
{
static void Main()
{
TwoDShape[] shapes = new TwoDShape[5]; shapes[0] = new Triangle("прямоугольный", 8.0, 12.0); shapes[1] = new Rectangle(10);
shapes[2] = new Rectangle(10, 4); shapes[3] = new Triangle(7.0);
shapes[4] = new TwoDShape(10, 20, "общая форма"); for (int i = 0; i < shapes.Length; i++)
{
Console.WriteLine("Объект — " + shapes[i].name);
Console.WriteLine("Площадь равна " + shapes[i].Area()); Console.WriteLine();
}
Console.ReadKey();
}
}
Обратите внимание на вызов разных конструкторов. Ниже приведен пример вывода.
Работа с файлами
В программах на языке С# при описании перемещения информации между устройствами часто применяется термин «поток» данных. В русском языке в контексте программирования под термином «поток» понимают два разных понятия: «Поток» от слова stream и «поток» от слова thread (виток). Поток от слова stream обозначает последовательную передачу данных между двумя устройствами, обычно между памятью компьютера и внешним устройством, например, с жестким диском. Направление передачи может быть от устройства в память (считывание), или, наоборот, из памяти в устройство (запись). Этот термин создает неопределенность, требующая для ее устранения дополнения словом «данных» («поток данных»). Поток от слова thread обозначает квант времени, выделяемый для обработки некоторому процессу.
Чернов Э. А. |
- 66 - |
Лекции по языку C# v 2.3 |
Представляется, что для исключения такой неоднозначности понятия «поток» в русском языке применять слово «файл».
Под файлом обычно подразумевают блок информации, который пересылается или может быть перемещен с одного устройства на другое. Файл имеет имя, на которое налагаются ограничения операционной системы
Имена файлов имеют расширения, указывающие на приложение, в котором файл был создан. Чаще всего файлы хранятся на внешних устройствах памяти, но их можно передать на печатающее устройство, в канал связи и т.д.
Обработка информации, содержащейся в файле, выполняется только в оперативной памяти программы, поскольку скорости обмена информацией с жестким диском на несколько порядков ниже быстродействия компьютера. Поэтому обычный порядок выполнения программы:
Считывание файла в оперативную память.
Обработка.
Сохранение файла на внешнем устройстве.
Хотя файлы могут быть связаны с разными устройствами (диски, флэш-память, компакт диски и т. д.), принцип обработки для них одинаков. Поэтому программирование операций ввода/вывода не зависит от устройства.
Рассматривают два вида файлов: байтовые и символьные. При передаче байтового файла содержимое байтов не учитывается, примером может служить файл, содержащий изображение, представляющий собой последовательность пикселей (может быть в сжатом виде). В символьном файле предусматривается интерпретация содержимого байтов, при этом учитываются не только информационные данные, но и управляющие символы (символы табуляции, признаки конца строки, конца файла и т. д.). Такой поток может содержать текст и числовые данные разных типов.
Одна и та же программа может работать с разными файлами, поэтому всегда надо указывать компьютеру, с каким именно файлом будет выполняться обработка в этот раз. Подключение файла к программе называется «открытием» файла. Открытие файла обеспечивает его связь с программой пользователя и запускает выполнение ряда операций, важнейшими из которых являются проверка на наличие на заданном устройстве указанного пользователем файла и готовность устройства к работе. Например, пользователь мог забыть вставить в компьютер флэш-память.
Чтобы с помощью одной программы можно было обрабатывать разные файлы, в программе объявляется «внутреннее» имя файла, которое может быть связано с любым файлом на диске. На это внутреннее имя файла налагаются требования, такие же как и для обычных имен переменных языка C#. На внешние имена файлов налагаются ограничения используемой операционной системы.
Для защиты информации от непреднамеренного разрушения (например, перезаписи файла другой информацией) пользователь должен всегда указывать, что он собирается делать с файлом: читать из него или писать в него.
Операции с файлом (чтение или запись) выполняются, начиная с текущей позиции в файле, которая непосредственно пользователем обычно не используется. Например, при вводе чисел из файла будет вводиться то число, которое размещено, начиная с текущей позиции в файле, и заканчивается последующим за числом разгра-

Чернов Э. А. |
- 67 - |
Лекции по языку C# v 2.3 |
ничителем («белым пробелом» (white space): пробелом, символом конца строки или символом табуляции). После выполнения этой операции текущая позиция автоматически устанавливается на следующее число.
Начальная установка текущей позиции в файле зависит от режима работы с файлом. При открытии файла в режиме чтения или записи текущая позиция устанавливается на первый байт файла. В режиме записи существующий файл переписывается новой информацией, т.е. старая информация уничтожается; если же файл не существует, то он будет создан, и в него будет занесена информация. В режиме добавления текущая позиция устанавливается на 1-й байт за последним символом файла, поэтому добавляемая информация будет приписана к концу существующего файла, если файл существует. В режиме ОpenOrCreate если файл не существует, он будет создан.
При считывании одного элемента данных (числа, символа, строки символов) указатель текущей позиции файла устанавливается на следующий подлежащий считыванию элемент.
Для всех операций с файлами созданы библиотеки классов, входящие в пространство имен System.IO. Перед началом работы с файлами надо обязательно подключить это пространство имен. Базовым является класс Stream, от которого наследуются все классы для работы с потоками.
Ниже перечислены наиболее часто используемые классы, необходимые при работе с файлами.
Имя класса |
Назначение и состав |
FileStream |
Класс для объявления байтового файла, допускающего как |
|
запись в него информации, так и считывание. |
StreamReader |
Класс, производный от класса FileStream и обеспечивающий |
|
считывание символьных данных из потока |
StreamWriter |
Класс, производный от класса FileStream и обеспечивающий |
|
запись символьных данных в поток. |
FileInfо |
Имеет функциональность класса FileStream, но позволяет |
|
также получить дополнительную информацию (время соз- |
|
дания, время обновления, атрибуты файла: архивный, сжа- |
|
тый, шифрованный и т. д.). |
DirectoryInfо |
Имеет возможности создания и удаления папок, но предос- |
|
тавляет также дополнительную информацию (время созда- |
|
ния, время обновления, атрибуты папки и т. д.). |
FileSystemInfо |
Это базовый класс для классов FileInfо и DirectoryInfо, пре- |
|
доставляет возможность одновременной работы как с фай- |
|
лами, так с и каталогами, переопределяя соответствующие |
|
методы. |
FileSystemWatcher |
Содержит обработчики событий для реакции на события для |
|
файловой системы. |
Принципиальной разницы для работы с байтовыми и символьными потоками нет. Особенностями байтовых потоков является невозможность просмотра их человеком, а также возможность записи информации с произвольного места. Эта возможность появилась в последних версиях языков программирования, и обеспечивает, например, средства для вставки звуковой дорожки в видеоклипы.

Чернов Э. А. |
- 68 - |
Лекции по языку C# v 2.3 |
Установка позиции в файле обеспечивает метод, имеющий формат:
имя_файла.Seek(long newPos, SeekOrigin pos)
Исходная позиция в файле для начала считывания или записи задает переменная pos типа SeekOrigin. От этой позиции отсчитывается смещение (в положительном или отрицательном направлении), заданное параметром newPos типа long. Благодаря такому типа переменной смещение может иметь значение до 4 Гбайт. Исходных позиций с помощью класса SeekOrigin может быть задано три:
Значение |
Описание |
SeekOrigin.Begin |
Исходная позиция - начало файла |
SeekOrigin.Current |
Исходной является текущая позиция в файле |
SeekOrigin.End |
Исходная позиция - конец файла |
После установки методом Seek текущей позиции в файле последующая операция чтения или записи выполняется с этой позиции.
Для работы с файлом, представляющего собой символьный поток, базовым является класс FileStream. У этого класса есть два производных (StreamReader и StreamWriter), которые предназначены для работы только с символьными файлами, причем первый из них допускает только чтение, а второй только запись. Поэтому нельзя одновременно читать из файла и писать в него.
Чтобы открыть файл, требуется объявить объект класса FileStream или (StreamReader или StreamWriter) с вызовом конструктора. Таких перегруженных конструкторов имеется несколько. Параметры конструктора задают все необходимые режимы и атрибуты. Далее будут рассмотрен только класс FileStream, который является универсальным.
Самый простой вариант оператора для открытия байтового файла имеет вид:
FileStream внутр_имя_файла = new FileStream (имя_файла, FileMode.режим);
Внешнее имя файла содержит кроме также полный путь к файлу. Обратите внимание, как прописан путь к файлу (две обратных косых черты):
FileStream MyFile = new FileStream ("C:\\Temp\\MyFile.txt", FileMode.Create);
Возможен и другой вариант (для неуправляемого кода):
FileStream MyFile = new FileStream (&"C:\Temp\MyFile.txt", FileMode.Create);
Обратите внимание на одинарные обратные косые черточки и символ '&' перед строкой, задающей путь к файлу.
где:
Заданное программистом внешнее имя файла, под которым файл сохранен на каком-либо устройстве. В конструкторе прописывается полный путь к файлу (с двойными обратными косыми чер-
имя_файла точками). Если путь прописывать нежелательно, файл должен быть в той же папке, в которой находится файл с расширением
.exe, либо подключен к проекту, как показано ниже (самый надежный способ, поскольку при переносе программы на другой компьютер ничего никуда копировать не надо).
Чернов Э. А. |
- 69 - |
Лекции по языку C# v 2.3 |
|
|
|
||
режим |
Записывается за ключевым словом FileMode после точки |
||
|
|
||
Append |
Новые данные добавляются в конец существующего файла. |
||
|
|
||
Create |
Создается новый файл. Если файл по этому пути уже есть, то он |
||
удаляется. |
|
||
|
|
||
|
|
||
CreateNew |
Создается новый файл при условии, что такой файл отсутствует. |
||
|
|
|
|
Open |
Открывает существующий файл. |
|
|
|
|
||
ОpenOrCreate |
Открывает существующий файл. Если файл отсутствует, то соз- |
||
дается новый. |
|
||
|
|
||
Truncate |
Открывает существующий файл, но текущая позиция устанавли- |
||
вается на начало файла, и длина файла устанавливается нулевой. |
|||
|
|||
|
|
|
Такое множество режимов предназначено для гибкого управления файлами. Например, режим CreateNew позволяет запретить создание нового файла, если такой файл уже есть. Раньше (например, в языке С++) существовало только три режима для открытия файла: открыть для чтения, открыть для записи и открыть для добавления.
Разнообразие режимов открытия файлов позволяет широко использовать обработку исключительных ситуаций, а наличие класса FileSystemWatcher еще более расширяет возможности управления файлами.
Для работы с файлами предусмотрены следующие исключительные ситуации, некоторые из которых перечислены ниже.
Исключение |
Смысл |
FileNotFoundException |
Указанный путь к файлу неправильный. |
IOException |
Нельзя открыть файл из-за ошибки файловой |
|
системы. |
ArgumentNullException |
Имя файла не задано. |
ArgumentException |
Неправильно указан режим. |
SecurityException |
Пользователю не разрешен доступ к файлу. |
DirectoryNotFoundException |
Ошибка в имени папки. |
Ошибки при обращении к файлу достаточно распространены, приведенные выше исключительные ситуации позволяют устранить (по возможности) проблему, например, повторив ввод имени файла или папки.
Класс FileStream предназначен для работы с байтовыми файлами. Для работы с символьными файлами используются классы StreamReader и StreamWriter.
Если открыт файл класса StreamReader, то для считывания данных из файла можно воспользоваться методами Read и ReadLine, которые использовались с классом Console. Аналогично, для класса StreamWriter применяют методы Write и WriteLine.
Простейшие конструкторы для этих классов в качестве параметра содержат только путь к имени файла.

Чернов Э. А. |
- 70 - |
Лекции по языку C# v 2.3 |
Вклассе StreamWriter имеется конструктор, параметрами которого кроме имени файла является также режим добавления. При значении этого параметра true данные добавляются к концу файла, при значении false файл переписывается с самого начала.
Вклассе StreamReader имеется конструктор, у которого кроме имени файла существует параметр Encoding для указания кодировки.
Ниже приведен пример, в котором данные вводятся в файл с клавиатуры и переписываются в файл.
static void Main(string[ ] args)
{
StreamWriter FileOut = new StreamWriter("C:\\Temp\\CrText.txt", true); Console.WriteLine("Введите текст");
string str = "a";
while (!str.Equals(""))// Пока не пустая строка
{
str = Console.ReadLine(); if(!str.Equals("")) FileOut.WriteLine(str);
} |
|
FileOut.Close(); |
// Закрыть файл |
}
В примере ввод с клавиатуры выполняется до тех пор, пока не будет введена пустая строка (вместо ввода нажатие на клавишу Enter). Если файла нет, он будет создан. Если файл существует, он будет переписан. (Проверка внутри цикла запрещает запись пустой строки)
Числа перед выводом в файл надо преобразовать в соответствующий тип (целые, с фиксированной точкой и т. д.). Ниже приведен пример.
static void Main(string[ ] args)
{
StreamWriter FileOut = new StreamWriter("C:\\Temp\\CrText.txt", false); double num = 0;
string str = ""; Console.WriteLine("Вводите числа"); while (num != -1)// Пока не -1
{
str = Console.ReadLine(); num = double.Parse(str);
if (num != -1)FileOut.WriteLine("{0,10:F2} ", num);
} |
|
FileOut.Close(); |
// Закрыть файл |
}
Вывод в файл для этой программы может иметь вид: