A string in C# is a sequence of UTF-16 characters (char values). It is a reference type, but it behaves like a value in the ways that matter: it is immutable, and == compares the text rather than the object identity. string is the C# keyword for the .NET type System.String; the two are interchangeable.
Output:
Porto
5
0
*****
hi
Porto has 5 letters
Strings are immutable
No method changes a string in place. ToUpper, Replace, Trim and every other method return a new string and leave the original untouched. Forgetting to use the return value is a very common string bug:
Output:
[ maya ]
[MAYA]
tea
tea and cake
Immutability makes strings safe to share between threads and to use as dictionary keys. The cost is that building a string piece by piece in a loop creates a new string at every step; for that, use StringBuilder.
Length and indexing
Length is a property (no parentheses). The indexer s[i] returns the char at position i, counting from 0, and is read-only:
Output:
7
A
1
IndexOutOfRangeException
XB-2931
C# 8 and later also accept an index from the end: code[^1] is the last character, the same as code[code.Length - 1]. To take several characters at once, see substring.
Length counts UTF-16 code units, not what a reader sees as characters. Most letters in most languages are one unit, but emoji and some rare scripts use two (a surrogate pair), so "👍".Length is 2.
Comparing strings
== and != compare the characters, case-sensitively. For anything else, pass a StringComparison value that says how to compare:
Output:
False
False
True
True
apple first
True
False
False
True
Which comparison to use:
Ordinalcompares the numeric character codes. It is fast, predictable and the same on every machine. Use it for identifiers, file names, keys, protocol values and anything not shown to a person.OrdinalIgnoreCaseis the same, ignoring case. The right choice for case-insensitive checks such as file extensions or HTTP headers.CurrentCulture(andCurrentCultureIgnoreCase) follows the rules of the user's language, so it sorts words the way a dictionary in that language would. Use it for sorting lists shown to users.InvariantCultureuses culture-aware rules that do not depend on the user's language settings. It still depends on the globalization library: modern .NET uses ICU, .NET Framework uses Windows' NLS, and the two can order some strings differently.
string.Compare(a, b) and a.CompareTo(b) without a StringComparison use the current culture, which makes their results vary between machines. Avoid comparing with a.ToLower() == b.ToLower(): it allocates two new strings, and it gives wrong answers in some cultures (the Turkish dotted and dotless i is the classic case).
Calling a.Equals(b) when a is null throws a NullReferenceException, while a == b and the static string.Equals(a, b) handle null safely.
Escape characters
Inside a regular string literal, a backslash starts an escape sequence:
| Escape | Meaning |
|---|---|
\n | New line (line feed) |
\r | Carriage return |
\t | Tab |
\\ | Backslash |
\" | Double quote |
\' | Single quote (needed only in a char literal) |
\0 | Null character |
\uXXXX | UTF-16 character by 4-digit hex code, e.g. \u00e9 |
\U0001F44D | Character by 8-digit hex code; above U+FFFF it becomes two char values |
\xH to \xHHHH | Character by 1 to 4 hex digits (avoid: the length is ambiguous) |
\a, \b, \f, \v | Alert, backspace, form feed, vertical tab |
\e | Escape character, U+001B (C# 13) |
Output:
Name: Ana
City: Porto
She said "hi"
C:\temp\report.txt
cafe = cafe
' is a quote char
An unrecognized sequence such as "\d" is a compile error (CS1009, unrecognized escape sequence), which is what happens when a Windows path or a regular expression is written as a regular string.
Verbatim strings and multiline strings
Prefixing a literal with @ makes it verbatim: backslashes are literal characters, and the string may span several lines. The only escape left is "", which produces one double quote.
Output:
C:\Users\ana\Documents\notes.txt
\d{3}-\d{4}
He said "ready" and left.
Rua das Flores 12
4050-262 Porto
Portugal
first line
second line
A verbatim multiline string includes everything between the quotes, including the indentation of the continuation lines, which is why they are usually written flush against the margin as above. The line breaks it contains are the ones in the source file, so a file saved with Windows line endings produces \r\n.
C# 11 added raw string literals, which solve both problems. They start and end with three or more double quotes, need no escaping at all, and remove the indentation that the closing quotes are indented by:
// C# 11 and later
string json = """
{
"name": "Ana",
"path": "C:\temp"
}
""";
// The 4 spaces before the closing """ are removed from every line.
If the content itself contains """, start and end with four quotes instead. Raw literals combine with interpolation too ($"""..."""); see string interpolation.
Null, empty and whitespace
A string variable can be null (no string at all), empty ("", length 0), or contain only whitespace. Two static helpers check these cases without risking a NullReferenceException:
Output:
null empty: True blank: True
"" empty: True blank: True
" " empty: False blank: True
"Ana" empty: False blank: False
True
For validating user input, string.IsNullOrWhiteSpace is almost always the one you want. string.Empty and "" are the same value; which one to write is style.
Looping over characters
A string is enumerable, so foreach visits each char. The char type has static methods to classify characters:
Output:
letters=6 digits=4 upper=1 symbols=1
!6202tesnuS
Joining strings
+ concatenates, and works with any type on the other side (it calls ToString()). For combining many values, prefer interpolation or string.Join:
Output:
Ana Silva, 31
Ana Silva, 31
AnaSilva
tea | coffee | juice
Total: 23
Total: 5
The last two lines show a precedence trap: + is evaluated left to right, so once the left side is a string, every following + is concatenation. Parenthesize arithmetic inside a concatenation.
Frequently Asked Questions
How do I get the length of a string in C#?
Use the Length property: "hello".Length is 5. It is a property, not a method, so there are no parentheses. Length counts UTF-16 code units, so an emoji or other character outside the Basic Multilingual Plane counts as 2. Calling it on a null string throws a NullReferenceException.
How do I compare strings in C#?
a == b compares the text and is case-sensitive. For case-insensitive equality use string.Equals(a, b, StringComparison.OrdinalIgnoreCase). To sort or order strings use string.Compare(a, b, StringComparison.Ordinal) or a culture-aware comparison, which returns a negative number, zero or a positive number.
How do I write a multiline string in C#?
Prefix the literal with @ to make it verbatim: it can span several lines, backslashes are literal, and a double quote is written as "". In C# 11 and later, raw string literals delimited by """ also span lines, strip the common indentation, and need no escaping at all. You can also join lines with \n or Environment.NewLine.
What is a verbatim string in C#?
A string literal prefixed with @, such as @"C:\Users\ana". Escape sequences are not processed, so a backslash is just a backslash, which makes it the natural choice for Windows paths and regular expressions. The only escape inside it is "" for a double quote.
What is the difference between string and String in C#?
None. string is the C# keyword alias for the .NET type System.String, just as int is an alias for System.Int32. Convention is to use string for declarations and string.IsNullOrEmpty(...) for static calls, but both compile to exactly the same thing.
What escape characters does C# support?
\n newline, \r carriage return, \t tab, \\ backslash, \" double quote, \' single quote, \0 null character, \uXXXX a UTF-16 character by hex code, \U0001F600 a character by 8-digit code (it becomes a surrogate pair), \a alert, \b backspace, \f form feed, \v vertical tab, and \e (C# 13) escape. An unknown sequence such as \d is a compile error (CS1009), which is why regular expressions are usually written as verbatim strings.