Nieprzetworzone literały stringowe

Wskazówka

Ten artykuł jest częścią sekcji Podstawy dla deweloperów, którzy już znają co najmniej jeden język programowania i uczą się języka C#. Jeśli dopiero zaczynasz programować, najpierw zacznij od samouczków Wprowadzenie . Aby uzyskać pełną gramatykę, zobacz dokumentację języka.

Pochodzi z innego języka? Literały nieprzetworzonych ciągów języka C# wypełniają tę samą rolę co ciągi Python i Rust's r"...", bloki tekstowe Java ("""...""") oraz ciągi szablonów znacznika wstecznego w językach JavaScript, TypeScript i Go. Składnia języka C# znajduje się najbliżej bloków tekstowych Java z dodatkowymi regułami ograniczników o zmiennej długości i interpolacji.

Literał surowego ciągu jest ograniczony trzema lub większą liczbą cudzysłowów podwójnych. Wewnątrz ograniczników każdy znak jest traktowany dosłownie. Cudzysłowy i ukośniki odwrotne nie wymagają ucieczki, a nowe linie są zachowywane zgodnie z zapisem. Używaj nieprzetworzonych ciągów dla dowolnego ciągu zawierającego cudzysłowy, ukośniki odwrotne lub wiele wierszy: JSON, XML, SQL, wyrażeń regularnych, ścieżek plików i przykładów kodu.

Ostrzeżenie

Surowy literał łańcuchowy ułatwia czytanie SQL, ale nie zwiększa bezpieczeństwa SQL. Nigdy nie łączy ani interpoluj wartości dostarczonych przez użytkownika do polecenia SQL. Ta praktyka powoduje otwarcie aplikacji na wstrzyknięcie kodu SQL. Zamiast tego użyj sparametryzowanych poleceń: DbCommand.CreateParameter z DbParameterCollection.Add lub pomocnikami wyższego poziomu w Entity Framework Core i Dapper. Ta sama ostrożność dotyczy innych formatów podatnych na wstrzyknięcie, takich jak polecenia powłoki, filtry LDAP i HTML.

Literał zawierający cudzysłowy i ukośniki odwrotne

Zwykły literał wymaga znaków ucieczki dla " i \. Nawet literał verbatim wymaga "", aby osadzić cytat. Surowy literał nie wymaga żadnego z:

// Same JSON value, three ways:
string regular  = "{ \"name\": \"Ada\", \"path\": \"C:\\\\src\" }";
string verbatim = @"{ ""name"": ""Ada"", ""path"": ""C:\\src"" }";
string raw      = """{ "name": "Ada", "path": "C:\\src" }""";

Console.WriteLine(regular  == raw);   // True
Console.WriteLine(verbatim == raw);   // True

Każda postać daje ten sam ciąg znaków, ale surowa wersja wygląda dokładnie tak jak odpowiadający jej zapis JSON.

Nieprzetworzone ciągi jednowierszowe

Ograniczniki otwierający i zamykający składają się każdy z co najmniej trzech podwójnych cudzysłowów, a ogranicznik zamykający musi zawierać tyle samo cudzysłowów co ogranicznik otwierający. Zawartość znajduje się między nimi w tej samej linii. Cudzysłowy i ukośniki odwrotne w treści należy traktować dosłownie:

// A raw string literal starts and ends with at least three quotes.
// Inside, " and \ are literal — no escaping required.
string message = """She said "hi" and left.""";
string regex   = """\d{3}-\d{4}""";

Console.WriteLine(message);   // She said "hi" and left.
Console.WriteLine(regex);     // \d{3}-\d{4}

Surowy ciąg jednoliniowy nie może być pusty pomiędzy ogranicznikami. Może kończyć się podwójnym cudzysłowem, ale nie może zaczynać się od jednego. Kompilator traktuje początkowy znak cudzysłowu podwójnego jako dodatkowy znak ogranicznika otwierającego. Jeśli treść musi zaczynać się od cudzysłowu, zamiast tego użyj wielowierszowego literału surowego ciągu znaków, w którym treść znajduje się w osobnym wierszu, dzięki czemu początkowy cudzysłów jest jednoznaczny.

Wielowierszowe nieprzetworzone ciągi

W przypadku treści wielowierszowej ogranicznik otwierający kończy wiersz, a ogranicznik zamykający rozpoczyna własny wiersz. Podobnie jak w przypadku surowych ciągów jednoliniowych ogranicznik składa się z trzech lub większej liczby podwójnych cudzysłowów, a ogranicznik zamykający musi zawierać tyle samo cudzysłowów co otwierający. Trzy cudzysłowy to najczęstszy przypadek, ale można użyć czterech, pięciu lub więcej, gdy sama treść zawiera ciąg """. Wszystko między dwoma ogranicznikami jest wartością ciągu, dokładnie tak jak zapisano:

// The opening """ and closing """ each sit on their own line.
// The content between them is the value, exactly as written.
string sql = """
    SELECT id, name
    FROM customers
    WHERE active = 1
    """;

Console.WriteLine(sql);

Znak nowego wiersza bezpośrednio po otwierającym znaczniku """ oraz znak nowego wiersza bezpośrednio przed zamykającym znacznikiem """ nie są częścią wartości. Są białymi znakami rozdzielającymi. Podobnie kompilator usuwa z każdej linii zawartości wszelkie białe znaki znajdujące się po lewej stronie zamykającego znacznika """, dzięki czemu można wciąć literał tak, aby pasował do bloku kodu, w którym się znajduje, bez uwzględniania tego wcięcia w ciągu znaków. W następnej sekcji szczegółowo omówiono tę regułę.

Jeśli sama zawartość zawiera przebieg """, użyj czterech lub większej liczby cudzysłowów dla ograniczników. Liczba separatorów musi jedynie być większa niż najdłuższy ciąg cudzysłowów w treści. Pełne zasady znajdziesz w temacie Literały ciągów pierwotnych (informacje o języku).

Wcięcie: ogranicznik zamykający ustawia margines

Kolumna zamknięcia """ definiuje lewy margines. Kompilator usuwa z każdego wiersza treści białe znaki aż do tej kolumny. Ta reguła umożliwia wcięcie literału w celu dopasowania otaczającego kodu bez zanieczyszczania wartości:

// The column of the closing """ sets a left margin.
// Whitespace up to that column is stripped from every content line.
string xml = """
        <order id="42">
            <item>book</item>
        </order>
        """;

// First content line begins at column 0 of the value:
Console.WriteLine(xml);
/* Output:
   <order id="42">
       <item>book</item>
   </order>
 */

Jeśli wiersz treści zawiera mniej wiodących znaków białych niż kolumna zamykającego ogranicznika, kompilator zgłasza błąd. Zachowaj wcięcie wszystkich wierszy treści co najmniej takie jak przy zamykającym znaczniku """.

Nieprzetworzone ciągi interpolowane

$ Dodaj prefiks do nieprzetworzonego ciągu, aby umożliwić interpolację. Wyrażenia w miejscach {} są obliczane, a ich wyniki są wstawiane do tej wartości:

// A single $ before """ enables interpolation: single { and } mark a hole.
// Inside a single-$ raw string, literal braces aren't allowed — use $$ when
// the content also contains literal { or }.
string name = "Ada";
int    score = 95;

string report = $"""
    Player:  {name}
    Score:   {score}
    Updated: {DateTime.UtcNow:yyyy-MM-dd}
    """;

Console.WriteLine(report);

Jeśli interpolowany ciąg ma również zawierać dosłowne znaki { lub }, zobacz Surowe literały ciągów (dokumentacja języka).

Kiedy wybrać który literał

Użyj surowego literału tekstowego, gdy zawartość zawiera cudzysłowy, ukośniki odwrotne lub wiele linii. W rezultacie jest krótszy, łatwiejszy do wklejania i kopiowania oraz pozbawiony błędów związanych z sekwencjami ucieczki.

Użyj zwykłego literału łańcuchowego w przypadku krótkich, jednoliniowych wartości bez cudzysłowów ani znaków ukośnika odwrotnego, takich jak nazwy, komunikaty i symbole zastępcze formatu.

Użyj literału ciągu dosłownego (@"...") tylko podczas pracy z istniejącym kodem korzystającym z nich. W nowym kodzie ciągi surowe obsługują wszystkie przypadki, które obsługują ciągi literałowe, a przy tym oferują prostszą składnię dla osadzonych cudzysłowów.

Zobacz także