BrandGhost
C# Multiline Strings: Verbatim Literals, Raw String Literals, and UTF-8 Literals

C# Multiline Strings: Verbatim Literals, Raw String Literals, and UTF-8 Literals

C# Multiline Strings: Verbatim Literals, Raw String Literals, and UTF-8 Literals

Working with c# multiline string syntax is something every .NET developer encounters regularly -- whether you are embedding SQL queries, JSON payloads, regular expressions, or HTML templates directly in code. C# has evolved significantly in this area. What once required awkward escape sequences and manual indentation management is now elegant and readable in modern .NET.

This guide covers all three major multiline and literal string techniques: verbatim strings (available since C# 2.0), raw string literals (C# 11 / .NET 6+), and UTF-8 string literals (C# 11 / .NET 7+). You will see when to use each, how they differ, and what pitfalls to avoid.


Why Multiline Strings Matter

Multi-line string content is ubiquitous in real applications:

  • SQL queries that span multiple lines
  • JSON/XML payloads for tests or configuration
  • Regular expressions with special characters
  • HTML or Markdown templates
  • GraphQL queries
  • File path strings with backslashes

Before C# 11, you had two choices: regular string literals with escape sequences (noisy and error-prone), or verbatim strings (cleaner, but with their own indentation issues). With C# 11, raw string literals solve nearly every remaining problem.


Verbatim String Literals (@"...")

Verbatim strings have been in C# since version 2.0. The @ prefix disables all escape sequence processing -- the only special sequence is "" (two double quotes) to represent a single double quote inside the string.

Basic Verbatim Strings

The verbatim string syntax has been a core C# feature since version 2.0, solving the problem of escape sequences in paths and other literal text. The @ prefix is all it takes:

var path = "C:\Users\Nick\Documents\file.txt";

// Verbatim string -- no escaping needed var verbatimPath = @"C:UsersNickDocumentsfile.txt";


### Multi-line Verbatim Strings

Verbatim strings capture newlines literally:

```csharp
var sql = @"
    SELECT u.Id, u.Name, u.Email
    FROM Users u
    WHERE u.IsActive = 1
    ORDER BY u.Name ASC";

Console.WriteLine(sql);

This works, but there is a well-known problem: indentation. The leading whitespace on each line is part of the string. If you indent the code for readability, the string contains the indentation. If you move the code to a different indentation level, the string content changes.

Verbatim Interpolated Strings

You can combine @ and $ for verbatim interpolated strings that embed expressions without needing to escape backslashes or special characters. In C# 6 and later, the $ prefix must come before @:

var tableName = "Users";
var status = 1;

var query = $@"
    SELECT *
    FROM {tableName}
    WHERE IsActive = {status}";

When to Use Verbatim Strings

Verbatim strings are still a solid choice in several specific situations. Reach for them when raw string literals are unavailable or when the content is short enough that indentation trimming is not a concern:

  • You are using .NET versions before C# 11
  • The content is simple (file paths, short multi-line text)
  • You need to embed double quotes occasionally

For everything else -- especially indented multi-line content -- raw string literals are superior.


Raw String Literals (C# 11 / .NET 6+)

Raw string literals were introduced in C# 11 (shipped with .NET 7, but available in .NET 6 projects using <LangVersion>11</LangVersion>). They solve the indentation problem cleanly and eliminate virtually all need for escape sequences.

Basic Syntax

Raw string literals start and end with three or more double quotes:

var greeting = """Hello, World!""";

var json = """
    {
        "name": "Nick",
        "role": "developer",
        "active": true
    }
    """;

The indentation trick is the key feature: the position of the closing """ determines the left margin. Any leading whitespace up to that column is stripped from every line. This means the string content is properly indented in your source code without polluting the runtime value.

Indentation Behavior

The position of the closing """ determines the left margin for the entire string. Any leading whitespace up to that column is automatically stripped from every line, keeping your source code indented without polluting the string value:

// Each line has 4 extra spaces stripped var xml = """ value """;

// Resulting string (no leading spaces): // // value //


This is a major improvement over verbatim strings. Your source code indentation and your string content are independent.

### Embedding Special Characters

Raw string literals require zero escaping for backslashes and double quotes, making them ideal for regex patterns, Windows paths, and HTML with quoted attributes:

```csharp
var regex = """^d{3}-d{4}$""";

var windowsPath = """C:UsersNickDocumentsfile.txt""";

var html = """
    <a href="https://www.devleader.ca" target="_blank">Dev Leader</a>
    """;

Embedding Quotes Inside Raw String Literals

If your content contains triple double quotes, use four (or more) double quotes as the delimiter:

// Content contains """
var withTripleQuotes = """"
    She said """Hello"""
    """";

// Content contains """"
var withFourQuotes = """""
    That is """"unusual""""
    """"";

The rule: use N+1 double quotes for your delimiter, where N is the maximum consecutive double quotes in your content.

Interpolated Raw String Literals

Combining $ with raw string literals gives you powerful embedded C# expressions inside multi-line content, with no escaping required. You can use $$ when the content contains literal curly braces (such as JSON):

var name = "Nick";
var items = new[] { "apple", "banana", "cherry" };
var count = items.Length;

var report = $"""
    User: {name}
    Items: {count}
    First: {items[0]}
    """;

You can use $$ to change the interpolation marker when the content contains single braces:

var jsonTemplate = $$"""
    {
        "name": "{{name}}",
        "count": {{count}}
    }
    """;

With $$, interpolation uses {{...}} instead of {...}, so JSON braces are treated as literal characters.


UTF-8 String Literals (.NET 7+)

UTF-8 string literals were introduced in C# 11 / .NET 7. They use the u8 suffix and produce a ReadOnlySpan<byte> containing the UTF-8 encoded bytes of the string -- computed at compile time, with zero runtime overhead.

Basic Syntax

UTF-8 string literals use the u8 suffix after a standard string literal. The compiler converts the string to UTF-8 bytes at compile time and produces a ReadOnlySpan<byte> -- no runtime encoding work required:

ReadOnlySpan contentType = "application/json"u8; ReadOnlySpan crlf = " "u8;


### Why UTF-8 Literals?

Many protocols and APIs work in UTF-8 bytes rather than .NET strings. Before UTF-8 literals, you had to either call `Encoding.UTF8.GetBytes()` at runtime (allocating a byte array) or manually hardcode a byte array literal. Both approaches are worse:

```csharp
// ❌ Pre-.NET 7: runtime encoding, allocates
byte[] bytes = Encoding.UTF8.GetBytes("Content-Type: application/json");

// ❌ Pre-.NET 7: hardcoded array, error-prone to maintain
byte[] manual = new byte[] { 67, 111, 110, 116, 101, 110, 116 }; // ...

// ✅ .NET 7+: compile-time, zero allocation
ReadOnlySpan<byte> modern = "Content-Type: application/json"u8;

Practical Uses

UTF-8 literals shine in protocol-level and I/O-heavy code where every allocation matters. The most common scenarios include:

  • HTTP request/response headers
  • Protocol buffer keys
  • Low-level I/O operations (writing headers to PipeWriter, Stream, etc.)
  • Constant ASCII tokens in parsers and serializers
  • Comparing incoming bytes against known values
namespace StringDemo;

public static class HttpHeaderParser
{
    private static readonly ReadOnlyMemory<byte> ContentTypeHeader =
        "Content-Type"u8.ToArray();

    private static readonly ReadOnlyMemory<byte> JsonMimeType =
        "application/json"u8.ToArray();

    public static bool IsJsonContentType(ReadOnlySpan<byte> headerName, ReadOnlySpan<byte> headerValue)
    {
        return headerName.SequenceEqual(ContentTypeHeader.Span)
            && headerValue.StartsWith(JsonMimeType.Span);
    }
}

Limitations of UTF-8 Literals

UTF-8 literals are powerful but narrowly scoped. Understanding their constraints prevents misuse -- here are the key ones to keep in mind:

  • The type is ReadOnlySpan<byte>, not string -- you cannot assign to a string variable
  • Multi-line UTF-8 literals are supported but less readable

For runtime conversion of strings to UTF-8 bytes, use Encoding.UTF8.GetBytes() or the Span-based overload Encoding.UTF8.TryGetBytes().


Comparison: Which String Literal to Use?

The table below maps common scenarios to the best string literal type. Use this as a quick reference when deciding which form fits your current situation:

|----------|-------------------| | File system paths | @"..." or """...""" | | Multi-line SQL, JSON, XML | """...""" (raw) | | Interpolated multi-line | $"""...""" | | Content with JSON {} braces | $$"""...""" | | UTF-8 protocol bytes | "..."u8 | | Legacy codebases (.NET < 6) | @"..." | | Simple single-line strings | "..." |


Real-World Example: Embedding a JSON Test Fixture

Here is a practical pattern from integration testing -- embedding a JSON payload cleanly using a raw string literal. This eliminates the messy escape sequences and manual indentation stripping that verbatim strings require:

namespace MyApp.Tests;

public sealed class UserApiTests
{
    private static readonly string CreateUserPayload = """
        {
            "name": "Nick Cosentino",
            "email": "[email protected]",
            "role": "Admin"
        }
        """;

    [Fact]
    public async Task CreateUser_WithValidPayload_Returns201()
    {
        var content = new StringContent(
            CreateUserPayload,
            System.Text.Encoding.UTF8,
            "application/json");

        // var response = await _client.PostAsync("/api/users", content);
        // response.StatusCode.ShouldBe(HttpStatusCode.Created);

        Assert.NotNull(content);
    }
}

Contrast this with the pre-C# 11 verbatim string version, which either has messy indentation or strips indentation manually.


Real-World Example: UTF-8 HTTP Response Headers

When writing custom middleware or low-level I/O code, UTF-8 literals enable efficient header writing:

namespace MyApp.Middleware;

public static class ResponseWriter
{
    // These are stored in the binary and read directly -- no runtime work
    private static ReadOnlySpan<byte> JsonContentType => "application/json; charset=utf-8"u8;
    private static ReadOnlySpan<byte> CacheControlNoStore => "no-store"u8;

    public static void WriteStandardHeaders(System.IO.Pipelines.PipeWriter writer)
    {
        var span = writer.GetSpan(JsonContentType.Length + CacheControlNoStore.Length + 10);
        JsonContentType.CopyTo(span);
        writer.Advance(JsonContentType.Length);
    }
}

This kind of approach matters in high-throughput services where every allocation counts. If you are building performance-sensitive systems, you will want to explore plugin architectures in C# to understand how to structure services that take advantage of these optimizations.


Multi-line Strings and Testing

Multi-line strings are especially useful in tests. Embedding expected JSON or XML as raw literals makes tests self-documenting and easy to update. This pairs well with testing feature slices in C#, where each feature has its own set of tests that directly embed the expected response payloads.

You might also use multi-line strings in CQRS with feature slices in C# when constructing query objects from string inputs -- raw string literals make the query definitions readable and maintainable.


Key Takeaways

Here is a quick summary of the most important concepts from this guide on multiline strings in C#:

  • Raw string literals ("""...""") solve indentation, eliminate most escaping, and support interpolation -- use them for all new multi-line content in C# 11+
  • UTF-8 literals ("..."u8) produce ReadOnlySpan<byte> at compile time with zero runtime cost -- essential for low-level, protocol-level byte handling
  • Combine $ with raw strings ($"""...""") for interpolated multi-line content
  • Use $$"""...""" when the content contains JSON-style curly braces

FAQ

Here are the most common questions developers ask about multiline strings and string literals in C#.

A verbatim string is prefixed with @ and treats the content literally -- backslashes are not escape characters. This makes it ideal for Windows file paths and any text that would otherwise require many escape sequences.

Are raw string literals available in .NET 6?

Raw string literals are a C# 11 language feature. You can use C# 11 with .NET 6 by setting <LangVersion>11</LangVersion> in your project file. The runtime behavior is the same; only the language version matters.

How does indentation work in raw string literals?

The position of the closing """ determines the left margin. All leading whitespace up to that column is stripped from every line. This lets you indent the raw literal to match your code's indentation without that whitespace becoming part of the string value.

What is the difference between u8 and regular string in C#?

A regular string is a UTF-16 .NET object on the heap. A u8 suffix literal is a ReadOnlySpan<byte> containing UTF-8 bytes, computed at compile time. It lives in the binary, not the heap, and is zero-cost at runtime.

Can I use UTF-8 literals for multi-line strings?

Yes, UTF-8 literals support the same multi-line syntax as regular strings. You can combine them with verbatim syntax: @"..."u8. However, they cannot be combined with raw string literal syntax in .NET 7 -- this limitation may be relaxed in future C# versions.

When should I prefer raw string literals over verbatim strings?

Use raw string literals whenever your string spans multiple lines, contains backslashes, or contains double quotes. Verbatim strings are still acceptable for simple single-line paths but raw literals are superior in every other case.

Do raw string literals affect performance?

No. Raw string literals are a compiler feature -- the string is stored in the binary exactly as it would be for a regular literal. There is no runtime cost. The only difference is how the source code is written.

Everything You Wanted to Know About Multiline Strings

What Is String Interpolation In C# - What You Need To Know

What is string interpolation in C#? Learn about its definition, syntax, and benefits for improving code readability and efficiency. Check out this guide!

C# Strings: Complete Guide to String Manipulation in .NET

Master C# string manipulation with this complete guide covering .NET 6-9 APIs, Span, raw literals, StringBuilder, and performance best practices.

An error has occurred. This application may no longer respond until reloaded. Reload