Все советы, размещенные здесь до сих пор, хороши и заслуживают внимания.
Одна вещь, на которой я хотел бы остановиться, это ваш вопрос: «Имеет ли обработка исключений, которые могут быть выброшены, снижение производительности по сравнению с упреждающим тестированием, например, существует ли файл на диске?»
Наивное эмпирическое правило гласит: «блоки try / catch стоят дорого». Это не совсем так. Попытка не дорогая. Это ловушка, когда система должна создать объект Exception и загрузить его с трассировкой стека, это дорого. Есть много случаев, когда исключение, ну, в общем-то, достаточно исключительное, так что вполне можно заключить код в блок try / catch.
Например, если вы заполняете словарь, это:
try
{
dict.Add(key, value);
}
catch(KeyException)
{
}
часто быстрее, чем это:
if (!dict.ContainsKey(key))
{
dict.Add(key, value);
}
для каждого добавляемого элемента, поскольку исключение выдается только при добавлении дублирующего ключа. (Сводные запросы LINQ делают это.)
В приведенном вами примере я бы использовал try / catch почти не задумываясь. Во-первых, тот факт, что файл существует, когда вы проверяете его, не означает, что он будет существовать, когда вы его откроете, поэтому вы все равно должны действительно обработать исключение.
Во-вторых, и я думаю, что более важно, если только вы a) ваш процесс не открывает тысячи файлов и b) шансы файла, который он пытается открыть, не существует, не являются достаточно низкими, снижение производительности при создании исключения не то, что вы когда-нибудь заметите. Вообще говоря, когда ваша программа пытается открыть файл, она пытается открыть только один файл. Это тот случай, когда написание более безопасного кода почти наверняка будет лучше, чем написание максимально быстрого кода.