C# Strings

A string is C#’s main type for storing and working with text. Names, messages, file paths, user input, JSON, and console output all use strings somewhere. Learning strings well matters because text looks simple, but details like immutability, escaping, casing, and comparison rules affect correctness and performance.

Overview: How C# Strings Work

In C#, string is an alias for the .NET type System.String. It is a reference type, but it behaves like a value in many everyday situations because strings are immutable: once a string object is created, its characters cannot be changed. Operations such as ToUpper(), Replace(), or concatenation create a new string instead of editing the old one.

A string is a sequence of char values. A char in .NET is a 16-bit UTF-16 code unit, which means most common characters fit in one char, but some Unicode characters, such as many emoji or historic scripts, may require two char values called a surrogate pair. For beginner work, indexing a string by position often feels like indexing characters, but for full Unicode text you should remember that one visible symbol is not always one char.

String literals are written between double quotes, such as "Hello". C# also supports escape sequences like \n for a newline and \" for a quote inside a string. Verbatim strings start with @ and treat backslashes as normal characters, which is useful for Windows paths. Interpolated strings start with $ and allow expressions inside braces. You can combine both as $@"..." or @$"...".

Because strings are so common, the C# compiler and CLR optimize them heavily. Literal strings may be interned, meaning identical literals can refer to the same object in memory. You should not rely on that for program logic; compare string contents with ==, Equals, or methods such as String.Equals with a StringComparison option. In C#, == on strings compares text content, not just object identity.

Syntax

string name = "Ada";
string message = $"Hello, {name}!";
int length = message.Length;
char first = message[0];
string changed = message.Replace("Hello", "Hi");
Syntax Meaning
"text" A regular string literal.
"Line 1\nLine 2" A string containing an escape sequence.
@"C:\Users\Ada" A verbatim string where backslashes are ordinary characters.
$"Hi {name}" An interpolated string that inserts expression values.
text.Length The number of UTF-16 char values in the string.
text[index] Reads the char at a zero-based index.
text.Substring(start, count) Creates a new string from part of another string.

The variable type is string, the variable name is chosen by you, and the value is usually a literal, another string expression, or a method result. String methods return values, so assign the result when you want to keep the changed text.

Examples

Creating and Combining Strings

using System;

class Program
{
    static void Main()
    {
        string firstName = "Grace";
        string lastName = "Hopper";
        string fullName = firstName + " " + lastName;
        string greeting = $"Welcome, {fullName}!";

        Console.WriteLine(fullName);
        Console.WriteLine(greeting);
        Console.WriteLine($"Characters: {fullName.Length}");
    }
}

Output:

Grace Hopper
Welcome, Grace Hopper!
Characters: 12

The + operator joins strings together. Interpolation is often clearer when text and variables are mixed, because each expression appears where its value will be placed. Length counts the characters in Grace Hopper, including the space.

Escapes, Verbatim Strings, and Quotes

using System;

class Program
{
    static void Main()
    {
        string twoLines = "First line\nSecond line";
        string path = @"C:\Course\cs\strings.txt";
        string quote = "She said, \"Keep practicing.\"";

        Console.WriteLine(twoLines);
        Console.WriteLine(path);
        Console.WriteLine(quote);
    }
}

Output:

First line
Second line
C:\Course\cs\strings.txt
She said, "Keep practicing."

The newline escape \n becomes an actual line break. The verbatim string keeps each backslash in the path without needing \\. The quote inside quote is escaped so the compiler knows it belongs inside the string instead of ending it.

Searching, Slicing, and Changing Text

using System;

class Program
{
    static void Main()
    {
        string email = "maya.chen@example.com";
        int atIndex = email.IndexOf('@');
        string userName = email.Substring(0, atIndex);
        string domain = email.Substring(atIndex + 1);
        string displayName = userName.Replace('.', ' ');

        Console.WriteLine($"User: {displayName}");
        Console.WriteLine($"Domain: {domain}");
        Console.WriteLine($"Ends with .com: {email.EndsWith(".com")}");
    }
}

Output:

User: maya chen
Domain: example.com
Ends with .com: True

IndexOf finds the position of a character. Substring creates new strings from ranges of the original email. Replace also returns a new string; the original email is unchanged.

Building Many Pieces Efficiently

using System;
using System.Text;

class Program
{
    static void Main()
    {
        string[] tasks = { "Read", "Practice", "Review" };
        StringBuilder builder = new StringBuilder();

        for (int i = 0; i < tasks.Length; i++)
        {
            builder.Append(i + 1);
            builder.Append(". ");
            builder.AppendLine(tasks[i]);
        }

        Console.Write(builder.ToString());
    }
}

Output:

1. Read
2. Practice
3. Review

Repeated string concatenation inside loops can create many temporary string objects. StringBuilder stores a growable buffer, so it is the usual choice when building text gradually from many pieces.

How Strings Work Step by Step

  1. The compiler sees a string literal such as "Ada" and records it as string metadata in the compiled assembly.
  2. At runtime, the CLR creates or reuses an immutable System.String object containing that text.
  3. When your code reads text[0], .NET returns the char at zero-based position 0. The string object itself is not changed.
  4. When your code calls text.Replace("a", "o"), .NET scans the old string and returns a new string containing the replacement result.
  5. If no variable stores that new string, the result is eventually discarded and the original string remains exactly as it was.

This design makes strings safe to share. If two variables refer to the same string object, one part of the program cannot secretly modify the text for the other part. The tradeoff is that heavy text editing can allocate many objects unless you use tools such as StringBuilder.

Common Mistakes

Trying to Modify a Character In Place

string word = "cat";
word[0] = 'b';

This does not compile because strings are immutable and the indexer is read-only. Create a new string instead.

using System;

class Program
{
    static void Main()
    {
        string word = "cat";
        string changed = "b" + word.Substring(1);
        Console.WriteLine(changed);
    }
}

Output:

bat

Forgetting That Methods Return New Strings

using System;

class Program
{
    static void Main()
    {
        string title = "c# strings";
        title.ToUpper();
        Console.WriteLine(title);

        title = title.ToUpper();
        Console.WriteLine(title);
    }
}

Output:

c# strings
C# STRINGS

The first ToUpper() result is ignored, so title still points to the original text. The second call assigns the returned string back to title.

Using Culture-Sensitive Comparisons Accidentally

using System;

class Program
{
    static void Main()
    {
        string savedCode = "FILE";
        string typedCode = "file";

        bool sameCode = string.Equals(savedCode, typedCode, StringComparison.OrdinalIgnoreCase);
        Console.WriteLine(sameCode);
    }
}

Output:

True

For identifiers, codes, file-like keys, protocol values, and other non-linguistic text, prefer ordinal comparisons. They compare Unicode values directly and avoid surprises from human-language casing rules.

Best Practices

  • Prefer string over System.String in ordinary C# code; they mean the same type, but string matches common style.
  • Use interpolation for readable messages, especially with more than one value.
  • Remember that string indexes are zero-based and that Length is a count, not the last valid index.
  • Assign the result of methods such as Trim(), Replace(), ToLower(), and ToUpper() when you want the changed text.
  • Use StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase for technical identifiers and exact program data.
  • Use StringBuilder when a loop builds a large string from many smaller parts.
  • Check for missing text with string.IsNullOrEmpty or string.IsNullOrWhiteSpace instead of comparing only to "".
  • Be careful with user-visible Unicode text: a visible character can be more than one char.

Practice Exercises

  1. Create variables for a first name and last name. Print the full name, the total length, and the first character of the last name.
  2. Given " learn C# strings ", trim the spaces, replace C# with CSharp, and print the final result.
  3. Write a small program that takes three words stored in an array and uses StringBuilder to print them as a numbered list.

Summary

  • string is C#’s standard type for text and is an alias for System.String.
  • Strings are immutable, so methods and concatenation create new strings.
  • Use escapes, verbatim strings, and interpolation to write readable string literals.
  • Length, indexing, IndexOf, Substring, Replace, and casing methods are everyday string tools.
  • Choose explicit comparison rules when string equality affects program behavior.
  • Use StringBuilder for repeated text construction in loops.