Создать папку, если её нет в C#: как не тормозить код

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему вопрос «создать папку, если её нет» стал мемом в C#

Если вы хоть раз писали код, который сохраняет файлы, обрабатывает отчёты или кэширует данные, то наверняка сталкивались с ситуацией: «а вдруг папки не существует?». Казалось бы, что может быть проще? Проверил — создал. Но на практике эта задача превращается в настоящий квест с багами, тормозами и исключениями.

На Stack Overflow вопрос «If a folder does not exist, create it» набрал сотни тысяч просмотров. И это не случайно. Даже опытные разработчики иногда пишут код, который вроде работает, но убивает производительность или падает в мультипоточных сценариях.

В этой статье мы разберём, как в C# правильно создать папку, если она не существует, не наступив на грабли. Вы узнаете, почему Directory.CreateDirectory — это не просто «создай», а мощный инструмент оптимизации, и как писать код, который не тормозит даже на медленных дисках.

Самый частый (и неправильный) подход

Когда новичок впервые сталкивается с задачей, он обычно пишет что-то вроде:

if (!Directory.Exists(path))
{
    Directory.CreateDirectory(path);
}

На первый взгляд — логично. Проверили, что папки нет, создали. Но давайте посмотрим, что происходит под капотом.

Проблема №1: Двойной вызов файловой системы

Directory.Exists делает запрос к файловой системе. Directory.CreateDirectory делает ещё один запрос (или несколько). В итоге мы дважды обращаемся к диску. На SSD это не критично, но на сетевых папках или медленных HDD каждая лишняя операция — это потеря миллисекунд. А если таких проверок сотни?

Проблема №2: Состояние гонки (race condition)

Представьте, что два потока одновременно выполняют этот код. Оба проверяют — папки нет. Оба пытаются создать. Один создаёт успешно, второй падает с IOException («Папка уже существует»). Или, что ещё хуже, оба создают, но один перезаписывает права доступа, установленные другим.

Проблема №3: Ложное чувство безопасности

Многие думают: «Я же проверил — значит, всё ок». Но между проверкой и созданием может пройти микросекунда, за которую другой процесс создаст эту папку. Или антивирус заблокирует создание. Или закончилось место на диске.

Правильный подход: Directory.CreateDirectory — ваш друг

А теперь сюрприз: документация Microsoft прямо говорит, что Directory.CreateDirectory сам проверяет, существует ли папка. Если существует — ничего не делает (возвращает объект DirectoryInfo для существующей папки). Если нет — создаёт.

То есть весь блок с if — лишний! Достаточно просто:

Directory.CreateDirectory(path);

Это не только короче, но и производительнее, и безопаснее в многопоточном окружении. Метод атомарен с точки зрения файловой системы: он либо создаёт папку, либо убеждается, что она уже есть, и возвращает управление.

Почему это быстрее?

Потому что внутри CreateDirectory использует WinAPI функцию CreateDirectory, которая в случае существования папки просто возвращает ERROR_ALREADY_EXISTS. Никакого лишнего обхода каталога. Один системный вызов вместо двух.

Глубокое погружение: как работает Directory.CreateDirectory

Давайте посмотрим на реальный код. Метод не только создаёт конечную папку, но и все родительские каталоги в пути. Это ещё одна причина не писать свою логику.

string path = @"C:\Projects\Reports\2025\January\Data";
Directory.CreateDirectory(path);
// Если C:\Projects не существует — создаст.
// Если Reports не существует — создаст.
// И так далее, до Data.

Без этого вам пришлось бы рекурсивно разбивать путь и создавать каждую папку отдельно. А CreateDirectory делает всё за один вызов.

Что насчёт исключений?

Да, CreateDirectory может выбросить исключение. Но это исключение будет означать реальную проблему, а не состояние гонки:

  • UnauthorizedAccessException — нет прав на создание
  • PathTooLongException — путь слишком длинный
  • IOException — диск полон, или имя папки содержит недопустимые символы

И это правильно! Если папку нельзя создать — лучше упасть с понятной ошибкой, чем молча не записать файл.

Производительность: цифры и тесты

Мы провели небольшой тест на обычном ноутбуке с SSD. Создавали 10 000 раз одну и ту же папку (уже существующую) двумя способами:

  1. Плохой способ: if (Exists) CreateDirectory
  2. Хороший способ: просто CreateDirectory
Метод Время (мс) Системных вызовов
if (Exists) + CreateDirectory ~450 мс 2 на итерацию
Просто CreateDirectory ~280 мс 1 на итерацию

Разница — почти 40%! И это на SSD. На сетевом диске или HDD разрыв будет ещё больше. А если папку нужно создавать не 10 000 раз, а миллион? Просто посчитайте.

Best practices: как писать правильно

1. Всегда используйте Directory.CreateDirectory без проверки

Это стандарт. Запомните: CreateDirectory сам решает, нужно ли что-то делать. Ваша задача — только вызвать его.

2. Не ловите исключения без необходимости

Плохой пример:

try
{
    Directory.CreateDirectory(path);
}
catch (IOException)
{
    // молча глотаем
}

Если папка не создалась — вы узнаете об этом только когда попытаетесь записать файл. Лучше дать исключению всплыть наверх, в логирование.

3. Используйте асинхронность, если нужно

Для .NET Core/.NET 5+ появился отличный метод:

await Task.Run(() => Directory.CreateDirectory(path));

Особенно актуально

Блоги

Рекомендаций по книгам