Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions docs/csharp/fundamentals/strings/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
title: "Strings in C#"
description: Learn how strings work in C# — declaration, immutability, literals (regular, verbatim, raw, interpolated), UTF-8 literals, and indexing.
ms.date: 05/08/2026
ms.topic: overview
ai-usage: ai-assisted
---

# C# strings

> [!TIP]
> This article is part of the **Fundamentals** section for developers who already know at least one programming language and are learning C#. If you're new to programming, start with the [Get started](../../tour-of-csharp/tutorials/index.md) tutorials first.
>
> **Coming from Java or C++?** A C# `string` is an immutable reference type backed by <xref:System.String?>. UTF-16 is the in-memory encoding, similar to Java's `String`. Unlike C/C++, strings aren't null-terminated and don't decay into pointers.

A *string* is a sequence of characters. In C#, `string` is the language keyword for the <xref:System.String?displayProperty=fullName> type. Every string literal you write produces a `System.String` instance.

## `string` vs. `String`

The `string` keyword and the `String` type name refer to the same type. They compile to identical intermediate language (IL).

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="StringKeyword":::

Prefer the `string` keyword in your own code. It's consistent with the other built-in type keywords (`int`, `bool`, `double`), and it works without a `using System;` directive.

## Strings are immutable

*Immutable* means the value can't be changed after it's created. Once you create a `string`, you can't change its characters. Methods such as `ToUpperInvariant`, `Replace`, `Substring`, and `Trim` return a *new* string that contains the modified value. The original instance stays the same.

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="Immutability":::

Because strings are immutable, you can safely share them across methods and threads. This immutability explains why the `string` type behaves like a value type in everyday use even though it's a reference type.

When you build a string from many small pieces in a loop, use <xref:System.Text.StringBuilder> to append in place and produce a single string at the end:

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="StringBuilder":::

## String literals

C# offers four literal forms. Each form suits different content. As a quick guide:

- Use **regular literals** for short, simple text with at most a few escape sequences.
- Use **verbatim literals** when backslashes dominate the content, such as Windows paths or regex patterns.
- Use **raw string literals** for multiline or structurally formatted text, such as inline JSON, SQL, XML, or formatted message blocks.
- Add a `$` prefix to any of the aforementioned literals to get an **interpolated string** when you need to embed values.
- Add a `u8` suffix to a literal to produce a UTF-8 byte sequence (a <xref:System.ReadOnlySpan%601> of `byte`) for byte-oriented APIs. See [UTF-8 string literals](../../language-reference/builtin-types/reference-types.md#utf-8-string-literals) in the language reference for details.

### Regular literals and escape sequences

A regular string literal is enclosed in double quotes. Backslash starts an escape sequence:

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="Escapes":::

Common escape sequences include `\n` (newline), `\t` (tab), `\"` (literal quote), `\\` (literal backslash), `\0` (null char), and Unicode escapes (`\uXXXX`, `\UXXXXXXXX`).

Beginning in C# 13, `\e` represents the **ESC** control character (U+001B). It's the start byte for ANSI terminal escape sequences:

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="EscEscape":::

Use a regular literal when the text is short and contains only a handful of escape sequences. Once the escapes start to outnumber the visible characters, switch to a verbatim or raw literal.

### Verbatim literals

A verbatim literal is prefixed with `@`. Backslashes are treated literally, which is useful for Windows paths and regular-expression patterns:

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="Verbatim":::

To embed a literal quote inside a verbatim string, double it: `@"She said ""hi""."`. Verbatim strings can also span multiple physical lines.

Verbatim literals are the right choice when backslashes are part of the content but you don't have many embedded quotes. For multiline text or content with quotes, raw string literals are usually clearer.

### Raw string literals

For any literal that contains quotes, backslashes, or multiple lines, prefer **raw string literals**. They eliminate escape noise entirely, which makes them the best fit for inline JSON, SQL, XML, regex patterns, and formatted text blocks where the source should look like the output:

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="Raw":::

Raw string literals all but eliminate escape sequences and accommodate any formatting and quoting you need. See [Raw string literals](raw-string-literals.md) for the full rules.

### Interpolated strings

A `$` prefix turns a literal into an *interpolated string*. Expressions in `{}` holes are evaluated and their results inserted, and you can apply standard format specifiers and alignment inside the holes. Interpolation also combines with the other literal forms — use `$@"..."` to interpolate a verbatim literal, or `$"""..."""` to interpolate a raw string literal for richly formatted output:

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="Interpolated":::

Interpolation is the recommended way to compose strings from values in everyday code.

## Indexing and `char`

A `string` is a sequence of UTF-16 *code units*. The indexer returns one <xref:System.Char?displayProperty=fullName>, which represents a single UTF-16 code unit, not necessarily a complete Unicode code point. `Length` returns the count of code units.

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="Indexing":::

For text that might contain emoji or characters outside the Basic Multilingual Plane, iterate by *rune* using <xref:System.Text.Rune> or by grapheme cluster using <xref:System.Globalization.StringInfo>. Plain `char` iteration works for most Western text and ASCII-dominant content.

## String equality

Equality on `string` compares the character sequences, not references:

:::code language="csharp" source="snippets/strings-overview/Program.cs" ID="EqualityIntro":::

For comparisons that need to be locale-aware or case-aware, pass an explicit <xref:System.StringComparison> value. Use `StringComparison.Ordinal` for protocol values, identifiers, and other non-linguistic text.

## Common string operations

Use the following table as a quick guide to everyday string operations in C#:

| Category | What it covers |
|-------------|-------------------------------------------------------------|
| Search | Find characters or substrings, test prefixes and suffixes |
| Split | Break a string into substrings on separators |
| Concatenate | Combine strings — `+`, interpolation, `Concat`, `Join` |
| Modify | Produce a transformed copy — `Replace`, `Trim`, `Substring` |
| Compare | Test equality and ordering with the right `StringComparison`|

The full API surface — every overload, every method — is documented in the <xref:System.String> reference.

## See also

- [Raw string literals](raw-string-literals.md)
- [`nameof` operator](nameof.md)
- <xref:System.String>
- <xref:System.Text.StringBuilder>
- <xref:System.Char>
- <xref:System.ReadOnlySpan%601>
80 changes: 80 additions & 0 deletions docs/csharp/fundamentals/strings/nameof.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
title: "The nameof operator in C#"
description: Use the nameof operator to capture an identifier as a compile-time string for argument validation, change notifications, attributes, and logging.
ms.date: 05/08/2026
ms.topic: concept-article
ai-usage: ai-assisted
---

# The `nameof` operator

> [!TIP]
> This article is part of the **Fundamentals** section for developers who already know at least one programming language and are learning C#. If you're new to programming, start with the [Get started](../../tour-of-csharp/tutorials/index.md) tutorials first. For the complete operator reference, see [`nameof`](../../language-reference/operators/nameof.md) in the language reference.
>
> **Coming from another language?** Other languages have similar features. Java's reflective `Class.getSimpleName()`, JavaScript's `Function.name` and `Object.keys`, Python's `__name__` and `vars()`, and Swift's `#function`/`#keyPath`. Unlike most of those, C#'s `nameof` is a pure compile-time construct. It uses no reflection, allocates nothing at runtime, and produces a constant `string` that's baked into the assembly.

The `nameof` operator returns the textual identifier of a symbol, such as a variable, parameter, type, member, or namespace, as a compile-time `string` constant. Anywhere you'd otherwise hardcode an identifier as a string, use `nameof`: the compiler verifies that the symbol exists, and rename refactorings update the result automatically.

## What `nameof` returns

`nameof` evaluates to the *final* identifier in its operand. It runs at compile time and has no runtime cost.

:::code language="csharp" source="snippets/nameof/Program.cs" ID="Basic":::

The operand can also be a *qualified expression*, one that uses the dot operator to navigate from a containing scope to a member, such as `customer.Name`, `System.Console`, or `List<int>.Enumerator`. In that case, only the last identifier is captured: `nameof(customer.Name)` returns `"Name"`, not `"customer.Name"`.

## Argument validation

The classic use is producing the parameter name in a thrown exception. Pass `nameof(parameter)` instead of the literal string `"parameter"` so a future rename can't leave the message lying:

:::code language="csharp" source="snippets/nameof/Program.cs" ID="GuardClause":::

For null checks specifically, prefer using exception helpers. These helpers, such as <xref:System.ArgumentNullException.ThrowIfNull*>, capture the argument's name automatically through <xref:System.Runtime.CompilerServices.CallerArgumentExpressionAttribute>, so a separate `nameof` isn't needed:

:::code language="csharp" source="snippets/nameof/Program.cs" ID="ThrowIfNull":::

Use `nameof` for the cases the helpers don't cover: <xref:System.ArgumentException>, <xref:System.ArgumentOutOfRangeException> (when validating something other than a single argument), and other guard messages.

## Property change notifications

Types that implement <xref:System.ComponentModel.INotifyPropertyChanged> raise an event whose payload includes the changed property's name. Hardcoding the name as a string creates a silent bug if the property is renamed and the string isn't. Use `nameof` instead:

:::code language="csharp" source="snippets/nameof/Program.cs" ID="PersonType":::

The setter calls `OnPropertyChanged(nameof(Name))` so the property name and the change notification stay in sync. Run the example to see the events fire:

:::code language="csharp" source="snippets/nameof/Program.cs" ID="PropertyChanged":::

## `nameof` in attribute arguments

`nameof` is valid inside attribute arguments. The compiler resolves identifiers in the surrounding scope, including the parameters of the method the attribute targets. This is the idiomatic way to refer to a parameter from an attribute such as <xref:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute>:

:::code language="csharp" source="snippets/nameof/Program.cs" ID="AttributeUsage":::

If the parameter is renamed, the `nameof` argument is updated by the same refactoring — the attribute can't fall out of date.

## Qualified names

For any qualified expression, `nameof` returns only the *last* identifier:

:::code language="csharp" source="snippets/nameof/Program.cs" ID="QualifiedName":::

If you need the fully qualified name, use <xref:System.Type.FullName?displayProperty=nameWithType> on a `Type` instance. `nameof` is for identifiers, not paths.

## Prefer `nameof` to identifier strings

Anywhere you refer to a method, property, parameter, type, or namespace by name in code, use `nameof` instead of a string literal. Compared to a hardcoded string:

- The compiler verifies that the symbol exists. A typo becomes a build error, not a silent runtime bug.
- Rename refactorings update the result automatically. Hardcoded strings drift out of sync.
- The result is a compile-time constant, so there's no runtime cost.

This recommendation applies to logging messages, exception arguments, attribute arguments, property-change notifications, and serialization key constants tied to a member name.

## See also

- [Strings overview](index.md)
- [Raw string literals](raw-string-literals.md)
- [`nameof` operator (language reference)](../../language-reference/operators/nameof.md)
- <xref:System.ArgumentNullException.ThrowIfNull*>
- <xref:System.ComponentModel.INotifyPropertyChanged>
76 changes: 76 additions & 0 deletions docs/csharp/fundamentals/strings/raw-string-literals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: "Raw string literals in C#"
description: Use raw string literals to write strings that contain quotes, backslashes, or multiple lines without escape sequences.
ms.date: 05/08/2026
ms.topic: concept-article
ai-usage: ai-assisted
---

# Raw string literals

> [!TIP]
> This article is part of the **Fundamentals** section for developers who already know at least one programming language and are learning C#. If you're new to programming, start with the [Get started](../../tour-of-csharp/tutorials/index.md) tutorials first. For the complete grammar, see the [language reference](../../language-reference/tokens/raw-string.md).
>
> **Coming from another language?** C# raw string literals fill the same role as Python's and Rust's `r"..."` strings, Java's text blocks (`"""..."""`), and the back-tick template strings in JavaScript, TypeScript, and Go. The C# syntax is closest to Java's text blocks, with extra rules for variable-length delimiters and interpolation.

A *raw string literal* is delimited by three or more double quotes. Inside the delimiters, every character is taken literally. Quotes and backslashes don't need escaping, and newlines are preserved as written. Use raw strings for any string that contains quotes, backslashes, or multiple lines: JSON, XML, SQL, regular expressions, file paths, and code samples.

> [!WARNING]
> A raw string literal makes SQL easier to read, but it doesn't make SQL safer. Never concatenate or interpolate user-supplied values into a SQL command. That practice opens your application to SQL injection. Use parameterized commands instead: <xref:System.Data.Common.DbCommand.CreateParameter*?displayProperty=nameWithType> with <xref:System.Data.Common.DbParameterCollection.Add*?displayProperty=nameWithType>, or the higher-level helpers in [Entity Framework Core](/ef/core/querying/raw-sql) and [Dapper](https://github.com/DapperLib/Dapper). The same caution applies to other injection-prone formats such as shell commands, LDAP filters, and HTML.

## A literal that contains quotes and backslashes

A regular literal needs escapes for `"` and `\`. A verbatim literal still needs `""` to embed a quote. A raw literal needs neither:

:::code language="csharp" source="snippets/raw-string-literals/Program.cs" ID="EscapeContrast":::

Each form produces the same string, but the raw version reads exactly like the JSON it represents.

## Single-line raw strings

The opening and closing delimiters are each at least three double quotes, and the closing delimiter must use the same number of quotes as the opening delimiter. The content sits between them on the same line. Quotes and backslashes inside the content are literal:

:::code language="csharp" source="snippets/raw-string-literals/Program.cs" ID="SingleLine":::

A single-line raw string can't be empty between its delimiters. It can end with a double quote, but it can't start with one. The compiler treats a leading double quote as an additional opening delimiter character. If your content must start with a quote, use a multiline raw string literal instead, which places the content on its own line where a leading quote is unambiguous.

## Multiline raw strings

For multiline content, the opening delimiter ends the line and the closing delimiter starts its own. As with single-line raw strings, the delimiter is three or more double quotes, and the closing delimiter must use the same number of quotes as the opening one. Three quotes is the common case, but you can use four, five, or more when the content itself contains a run of `"""`. Everything between the two delimiters is the value of the string, exactly as written:

:::code language="csharp" source="snippets/raw-string-literals/Program.cs" ID="Multiline":::

The newline immediately after the opening `"""` and the newline immediately before the closing `"""` aren't part of the value. They're delimiter whitespace. Likewise, the compiler strips any whitespace to the left of the closing `"""` from every content line, so you can indent the literal to match its enclosing code block without that indentation appearing in the string. The next section covers this rule in detail.

If the content itself contains a run of `"""`, use four or more quotes for the delimiters. The delimiter count just has to exceed the longest run of quotes in the content. See [Raw string literals (language reference)](../../language-reference/tokens/raw-string.md) for the full rules.

## Indentation: the closing delimiter sets the margin

The column of the closing `"""` defines a left margin. The compiler strips whitespace up to that column from every content line. This rule lets you indent the literal to match the surrounding code without polluting the value:

:::code language="csharp" source="snippets/raw-string-literals/Program.cs" ID="Indentation":::

If a content line has fewer leading whitespace characters than the closing delimiter's column, the compiler reports an error. Keep all content lines indented at least as much as the closing `"""`.

## Raw interpolated strings

Add a `$` prefix to a raw string to enable interpolation. The expressions in `{}` holes are evaluated, and their results are inserted into the value:

:::code language="csharp" source="snippets/raw-string-literals/Program.cs" ID="RawInterpolated":::

If your interpolated content also needs literal `{` or `}` characters, see [Raw string literals (language reference)](../../language-reference/tokens/raw-string.md).

## When to choose which literal

Use a **raw string literal** whenever the content contains quotes, backslashes, or multiple lines. The result is shorter to read, easier to paste in or out of, and free of escape-sequence bugs.

Use a **regular string literal** for short, single-line values without quotes or backslashes such as names, messages, format placeholders.

Use a **verbatim string literal** (`@"..."`) only when working with existing code that uses them. For new code, raw strings cover every case that verbatim strings cover, with cleaner syntax for embedded quotes.

## See also

- [Strings overview](index.md)
- [`nameof` operator](nameof.md)
- [Raw string literals (language reference)](../../language-reference/tokens/raw-string.md)
- <xref:System.String>
Loading
Loading