C# String Methods

C# string methods are built-in operations for inspecting, cleaning, changing, and comparing text. They matter because most programs receive text from users, files, web APIs, or databases, and that text is rarely already in the exact form you need. Once you understand which method to use and what it returns, you can process strings clearly without writing character-by-character code for every task.

Overview: How String Methods Work

In C#, string is an alias for System.String, so string methods are methods on the .NET String type. Common examples include Trim(), Contains(), IndexOf(), Substring(), Replace(), Split(), ToUpper(), and ToLower(). Some methods answer a question and return bool, some return a number, some return a new string, and some return an array of strings.

The most important rule is that strings are immutable. A method such as name.Trim() does not edit name in place. It returns a new string containing the trimmed text. If you want to keep the result, assign it to a variable: name = name.Trim();. This design lets the CLR safely share string objects, including interned string literals, because no caller can secretly change the text for another caller.

Internally, most string methods scan the string’s UTF-16 char values. Length counts UTF-16 code units, and indexes are zero-based. Methods that search, such as IndexOf and Contains, look for a matching sequence and may return -1 when nothing is found. Methods that create modified text allocate a new string object for the result. For small strings this is usually fine; for heavy repeated editing in loops, allocation can become a performance concern and StringBuilder may be a better tool.

String methods also involve comparison rules. Some overloads use the current culture by default, while others let you pass StringComparison. Culture-sensitive comparisons are useful for words displayed to users. Ordinal comparisons are usually better for program data such as IDs, file extensions, command names, email domains, and protocol tokens, because they compare Unicode values directly and consistently.

Syntax

string text = "  old word, new word  ";
string cleaned = text.Trim();
bool hasWord = text.Contains("word", StringComparison.OrdinalIgnoreCase);
int position = text.IndexOf("word", StringComparison.Ordinal);
string part = cleaned.Substring(0, 3);
string changed = cleaned.Replace("old", "first");
string[] pieces = cleaned.Split(',');
string combined = string.Join(" | ", pieces);
Method Returns Use it for
Trim(), TrimStart(), TrimEnd() string Removing whitespace from the whole string or one side.
Contains(value) bool Checking whether text appears somewhere.
StartsWith(value), EndsWith(value) bool Checking prefixes and suffixes.
IndexOf(value), LastIndexOf(value) int Finding the position of text, or -1 if missing.
Substring(start, length) string Copying part of a string by position.
Replace(old, new) string Creating a string with matching text changed.
Split(separator) string[] Breaking one string into many parts.
string.Join(separator, values) string Combining many values into one string.

The variable before the dot is the string instance. The method name chooses the operation. Parentheses contain arguments such as the text to find, the replacement text, or comparison options. Methods that return a value must be used in an expression, printed, tested, or assigned.

Examples

Cleaning and Normalizing Input

using System;

class Program
{
    static void Main()
    {
        string raw = "  Ada Lovelace  ";
        string cleaned = raw.Trim();
        string upper = cleaned.ToUpperInvariant();

        Console.WriteLine($"Raw length: {raw.Length}");
        Console.WriteLine($"Cleaned: [{cleaned}]");
        Console.WriteLine($"Code form: {upper.Replace(" ", "_")}");
    }
}

Output:

Raw length: 16
Cleaned: [Ada Lovelace]
Code form: ADA_LOVELACE

Trim() removes leading and trailing whitespace but leaves the original raw string unchanged. ToUpperInvariant() creates a culture-invariant uppercase version, and Replace() creates another new string where spaces become underscores. This is common when turning human text into a predictable code-like value.

Searching Before Slicing

using System;

class Program
{
    static void Main()
    {
        string email = "sam.rivera@example.org";
        int at = email.IndexOf('@');

        if (at >= 0)
        {
            string user = email.Substring(0, at);
            string domain = email.Substring(at + 1);

            Console.WriteLine($"User: {user}");
            Console.WriteLine($"Domain: {domain}");
            Console.WriteLine($"Organization domain: {domain.EndsWith(".org", StringComparison.OrdinalIgnoreCase)}");
        }
    }
}

Output:

User: sam.rivera
Domain: example.org
Organization domain: True

IndexOf('@') returns the zero-based position of the at sign. The code checks for -1 before calling Substring(), because slicing with a missing marker would use invalid indexes. The suffix check uses StringComparison.OrdinalIgnoreCase because a domain suffix is technical text, not a word being sorted for a reader.

Splitting, Trimming, and Joining Values

using System;

class Program
{
    static void Main()
    {
        string csv = "red, green,blue, yellow";
        string[] colors = csv.Split(',');

        for (int i = 0; i < colors.Length; i++)
        {
            colors[i] = colors[i].Trim();
        }

        string display = string.Join(" | ", colors);
        Console.WriteLine(display);
        Console.WriteLine($"Items: {colors.Length}");
    }
}

Output:

red | green | blue | yellow
Items: 4

Split(',') creates an array by cutting the string at each comma. Because the original text has inconsistent spaces, the loop trims each element. string.Join() then builds one readable display string with a consistent separator.

Replacing Text Without Changing the Original

using System;

class Program
{
    static void Main()
    {
        string template = "Hello, NAME. Your order ORDER_ID is ready.";
        string message = template.Replace("NAME", "Mina")
                                 .Replace("ORDER_ID", "A104");

        Console.WriteLine(template);
        Console.WriteLine(message);
    }
}

Output:

Hello, NAME. Your order ORDER_ID is ready.
Hello, Mina. Your order A104 is ready.

Each Replace() call returns a new string, so the calls can be chained. The original template remains available, which is useful when the same pattern will be reused with different values.

How String Methods Work Step by Step

  1. Your code stores a reference to an immutable System.String object.
  2. When you call a method such as Trim(), the CLR passes that object reference and any arguments to the method implementation.
  3. The method inspects the string’s internal UTF-16 characters. For Trim(), it finds the first and last non-whitespace positions. For IndexOf(), it scans for a matching character or sequence.
  4. If the result needs new text, .NET allocates a new string and copies the required characters into it. If a method only answers a question, it returns a bool or int instead.
  5. Your variable still refers to the old string unless you assign the returned value to that variable or another variable.

This explains both the safety and the cost of string methods. They are safe because they do not mutate shared text. They can cost memory when many new strings are created, so avoid unnecessary repeated modifications inside large loops.

Common Mistakes

Forgetting to Store the Returned String

using System;

class Program
{
    static void Main()
    {
        string title = "  C# Methods  ";
        title.Trim();
        Console.WriteLine($"[{title}]");

        title = title.Trim();
        Console.WriteLine($"[{title}]");
    }
}

Output:

[  C# Methods  ]
[C# Methods]

The first call calculates trimmed text and then discards it. The corrected line assigns the returned string back to title.

Using Substring() Without Checking the Position

string fileName = "notes";
int dot = fileName.IndexOf('.');
string extension = fileName.Substring(dot + 1);

This compiles, but it is logically wrong for filenames without a dot. IndexOf() returns -1, so dot + 1 becomes 0, and the whole filename is treated as the extension. Check the marker first.

using System;

class Program
{
    static void Main()
    {
        string fileName = "notes";
        int dot = fileName.LastIndexOf('.');
        string extension = dot >= 0 ? fileName.Substring(dot + 1) : "(none)";

        Console.WriteLine(extension);
    }
}

Output:

(none)

Lowercasing Just to Compare

using System;

class Program
{
    static void Main()
    {
        string command = "Start";
        bool isStart = command.Equals("start", StringComparison.OrdinalIgnoreCase);

        Console.WriteLine(isStart);
    }
}

Output:

True

Calling ToLower() on both strings creates extra strings and may use culture-sensitive casing rules unless you choose an invariant method. For command words, keys, and other program data, compare directly with StringComparison.OrdinalIgnoreCase.

Best Practices

  • Remember that methods such as Trim(), Replace(), ToUpper(), and Substring() return new strings.
  • Check the result of IndexOf() or LastIndexOf() before using it to calculate a substring.
  • Use StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase for technical values such as IDs, commands, domains, and file extensions.
  • Use ToUpperInvariant() or ToLowerInvariant() when you truly need a normalized stored form.
  • Use string.IsNullOrEmpty() or string.IsNullOrWhiteSpace() before calling methods on text that may be missing.
  • Prefer Split() and Join() for simple separated values, but use a real CSV parser for quoted CSV files with commas inside fields.
  • Avoid long chains of string-changing methods when each intermediate result matters; use named variables to make the steps readable.
  • For many repeated edits in a loop, consider StringBuilder instead of creating a new string at every step.

Practice Exercises

  1. Start with " learn,csharp,strings ". Trim the outside spaces, split on commas, capitalize each word with ToUpperInvariant(), and join them with " - ".
  2. Write a program that stores an email address and prints the username and domain. If the address does not contain @, print Invalid email.
  3. Given a filename such as "report.final.pdf", use LastIndexOf() and Substring() to print only the extension. Also handle a filename with no dot.

Summary

  • String methods are operations on System.String for searching, slicing, cleaning, replacing, splitting, joining, and comparing text.
  • C# strings are immutable, so methods that appear to change text return a new string instead.
  • IndexOf() returns -1 when text is not found; check that before slicing.
  • Split() turns one string into an array, while string.Join() turns many values into one string.
  • Use explicit StringComparison values when comparison correctness matters.
  • Choose readable method chains for small transformations and more deliberate code for complex text processing.