Avatar ·

Многострочные строковые литералы в C#: как объявить строку с переносами?

Всем привет! Я начинающий C# разработчик, перешел с Python, и столкнулся с проблемой, которая кажется мне очень неудобной.

В Python я мог легко создавать многострочные строки с помощью тройных кавычек:

text = """Это многострочная строка.
Она может содержать переносы строк.
И это очень удобно."""

Но в C# я не могу найти прямого аналога. Когда я пытаюсь записать длинный SQL-запрос, JSON или просто текст с переносами, я получаю ошибки компиляции или мне приходится использовать костыли.

Вот пример того, что я хочу сделать:

string query = "SELECT * 
FROM Users 
WHERE Age > 18";

Очевидно, что так писать нельзя — компилятор ругается. Я знаю про конкатенацию строк и про @-строки (verbatim string), но они не решают проблему полностью.

Подскажите, есть ли в C# 7, 8, 9 или более новых версиях нормальная поддержка многострочных строковых литералов? Как правильно объявлять длинные строки с переносами в C#, чтобы код оставался читаемым?

Буду благодарен за примеры с объяснением.

<<>>

Решение: Многострочные строки в C# 11 и новее (сырые строковые литералы)

Начиная с C# 11 (.NET 7), в языке появилась полноценная поддержка многострочных строковых литералов — сырые строковые литералы (raw string literals). Это именно то, что вы ищете, и они очень похожи на тройные кавычки из Python.

Синтаксис простой: используйте три двойные кавычки """ для открытия и закрытия строки. При этом:

  • Строка может содержать любые символы, включая переносы строк, кавычки и обратную косую черту.
  • Отступы (пробелы и табуляции) автоматически выравниваются по самому левому символу содержимого.
  • Не нужно экранировать кавычки внутри строки (если их меньше двух подряд).

Пример 1: Простой SQL-запрос

string query = """
    SELECT *
    FROM Users
    WHERE Age > 18
    """;
    
Console.WriteLine(query);
// Вывод:
//     SELECT *
//     FROM Users
//     WHERE Age > 18

Пример 2: JSON с отступами

string json = """
    {
        "name": "John",
        "age": 30,
        "city": "New York"
    }
    """;
    
Console.WriteLine(json);

Важно про отступы: Закрывающие кавычки """ определяют уровень отступа. Если вы поставите их на том же уровне, что и содержимое, отступов не будет. Если сдвинете влево — отступы обрежутся. Рекомендуется ставить закрывающие кавычки на том же уровне, что и содержимое.

Что было до C# 11? (Если вы используете старую версию)

Если у вас C# 10 или старше, есть два основных подхода:

Вариант A: Verbatim string с конкатенацией

string query = @"SELECT * 
FROM Users 
WHERE Age > 18";

Минус: переносы строк в коде становятся частью строки. Если нужно убрать отступы, придётся писать всё с начала строки (без отступов), что ухудшает читаемость.

Вариант B: Интерполированные строки (с $)

string query = $@"SELECT * 
FROM Users 
WHERE Age > 18";

Работает, но то же ограничение по отступам.

Вариант C: StringBuilder для очень больших строк

var sb = new StringBuilder();
sb.AppendLine("SELECT *");
sb.AppendLine("FROM Users");
sb.AppendLine("WHERE Age > 18");
string query = sb.ToString();

Рекомендация

Если у вас .NET 7 или новее — используйте сырые строковые литералы ("""..."""). Это самый чистый и современный способ. Если проект на старой версии — используйте @"" с конкатенацией, но будьте готовы к проблемам с форматированием.

<<>>

Альтернативный подход: Использование StringBuilder с файлами ресурсов

Если вы часто работаете с большими многострочными строками (например, SQL-запросами, HTML-шаблонами или JSON-конфигами), я рекомендую выносить их в отдельные файлы или использовать ресурсы (.resx). Это сделает код чище и упростит поддержку.

Пример с файлом ресурсов:

  1. Создайте файл ресурсов (например, Queries.resx) в проекте.
  2. Добавьте в него строку с именем GetUsersQuery и значением вашего SQL-запроса.
  3. Используйте его в коде:
string query = Properties.Resources.GetUsersQuery;
// Или, если используете глобальные using:
string query = Queries.GetUsersQuery;

Преимущества этого подхода:

  • Строки не засоряют код.
  • Легко менять запросы без перекомпиляции всего проекта (если использовать внешние файлы).
  • Поддержка локализации.

Альтернатива для JSON: Храните JSON в отдельных .json файлах и загружайте их через File.ReadAllText() или встроенные методы сериализации. Это особенно удобно для тестовых данных.

Таким образом, если вам не нужны динамические строки с интерполяцией, выносите многострочные данные из кода — это улучшит читаемость и поддерживаемость проекта.