Console.WriteLine(exc.Message) ;
} finally {
fin.Close() ;
}
}
}
Обратите внимание на то, что в приведенной выше программе применяются два блока try
. В первом из них перехватываются исключения, возникающие при вводе-выводе и способные воспрепятствовать открытию файла. Если произойдет ошибка ввода-вывода, выполнение программы завершится. В противном try
будет продолжен контроль исключений, возникающих в операциях ввода-вывода. Следовательно, второй блок try
выполняется только в том случае, если в переменной fin
содержится ссылка на открытый файл. Обратите также внимание на то, что файл закрывается в блоке finally
, связанном со вторым блоком try
. Это означает, что независимо от того, как завершится цикл do-while
(нормально или аварийно из-за ошибки), файл все равно будет закрыт. И хотя в данном конкретном примере это и так важно, поскольку программа все равно завершится в данной точке, преимущество такого подхода, вообще говоря, заключается в том, что файл закрывается в завершающем блоке finally
в любом случае — даже если выполнение кода доступа к этому файлу завершается преждевременно из-за какого-нибудь исключения.
В некоторых случаях оказывается проще заключить те части программы, где осуществляется открытие и доступ к файлу, внутрь блока try
, вместо того чтобы разделять обе эти операции. В качестве примера ниже приведен другой, более краткий вариант написания представленной выше программы ShowFile
.
// Отобразить содержимое текстового файла.
using System;
using System.IO;
class ShowFile {
static void Main(string[] args) {
int i;
FileStream fin = null;
if (args.Length != 1) {
Console.WriteLine("Применение: ShowFile File");
return;
}
try {
fin = new FileStream(args[0], FileMode.Open);
// Читать байты до конца файла,
do {
i = fin.ReadByte();
if(i != -1) Console.Write((char) i);
} while (i != -1);
} catch(IOException exc) {
Console.WriteLine("Ошибка ввода-вывода:\n" + exc.Message);
} finally {
if(fin != null) fin.Close();
}
}
}
Обратите внимание на то, что в данном варианте программы переменная fin
ссылки на объект класса FileStream
инициализируется пустым значением. Если файл удастся открыть в конструкторе класса FileStream
, то значение переменной fin
окажется непустым, а иначе — оно так и останется пустым. Это очень важно, поскольку метод Close()
вызывается внутри блока finally
только в том случае, если значение переменной fin
оказывается непустым. Подобный механизм препятствует любой попытке вызвать метод Сlose()
для переменной fin
, когда она не ссылается на открытый файл. Благодаря своей компактности такой подход часто применяется во многих примерах организации ввода-вывода, приведенных далее в этой книге. Следует, однако, иметь в виду, что он не пригоден в тех случаях, когда ситуацию, возникающую в связи с невозможностью открыть файл, нужно обрабатывать отдельно. Так, если пользователь неправильно введет имя файла, то на экран, возможно, придется вывести приглашение правильно ввести имя файла, прежде чем входить в блок try
, где осуществляется проверка правильности доступа к файлу.
В целом, порядок открытия, доступа и закрытия файла зависит от конкретного приложения. То, что хорошо в одном случае, может оказаться неприемлемым в другом. Поэтому данный процесс приходится приспосабливать к конкретным потребностям разрабатываемой программы.
Для записи байта в файл служит метод WriteByte()
. Ниже приведена его простейшая форма.
void WriteByte(byte value)
Этот метод выполняет запись в файл байта, обозначаемого параметром NotSupportedException
. А если поток закрыт, то генерируется исключение ObjectDisposedException
.
Для записи в файл целого массива байтов может быть вызван метод Write()
. Ниже приведена его общая форма.
void Write(byte[] array, int offset, int count)