C# from the ground up

Text, interpolation and formatting

Building lines from values, the dollar sign that turns a string into a template, format specifiers, and the everyday string methods.

Part 3 of 13

Beginner15 min readBack to Courses

Joining pieces into one line

Text is a value like any other, and the plus sign joins two pieces into a larger one: "Hello, " + name + "!". The plus is doing double duty — it adds numbers and it glues strings — but never both at once. If either side is text, the other side is turned into text first, which is why "Visits: " + 3 reads well and 1 + 2 + " apples" gives 3 apples while "apples " + 1 + 2 gives apples 12.

Joining a few pieces is fine. When a line needs a value in four places, the plus signs start to hide the shape of the sentence, and that is the case interpolation exists for.

Interpolation: a hole in the text

Put a dollar sign in front of a double-quoted string and it becomes a template: $"Hello, {name}!". Anything inside braces is worked out and placed in the string, so a hole holds an expression rather than only a name: $"Rows: {items.Count}" prints however many items there are.

The dollar sign marks the whole string, and a string without it treats braces as ordinary characters. That is the one thing to remember about the syntax: the same text behaves differently depending on one character at the front.

Formatting inside the hole

A colon inside a hole adds a format: $"Total: {price:F2}" prints the price with two decimal places. F2 means fixed point, two digits after the point. Other letters exist — N0 for a counted number with thousands separators, P0 for a percentage — and the same text works outside a hole as price.ToString("F2").

Formatting changes the text, never the value. The number that goes on to be added to something else is the one you started with, so it is safe to format on the way out of a program and never in the middle of an arithmetic run.

A width after a comma lines values up in columns. {name,-12} reserves twelve characters and left-aligns the text; {price,10:F2} reserves ten and right-aligns the number, which is why a column of prices ends on the same edge. A negative width means left, a positive one means right, and a format letter can follow the width after a colon. The receipt example below uses both to build a slip that stays in line as the values change.

A string is a value with methods

using System;

class Program
{
    static void Main()
    {
        var name = "Ada Lovelace";
        var price = 12.5m;

        Console.WriteLine($"Hello, {name}!");
        Console.WriteLine($"Total: {price:F2}");
        Console.WriteLine($"Twice: {price * 2:F2}");

        Console.WriteLine(name.Length);           // 12
        Console.WriteLine(name.ToUpper()); // ADA LOVELACE
        Console.WriteLine(name.Substring(0, 3));  // Ada
        Console.WriteLine(name.Contains("Ada"));  // True
        Console.WriteLine(name.Replace("Ada", "G"));

        var parts = "tea, cake, jam".Split(',');
        Console.WriteLine(parts.Length);          // 3
        Console.WriteLine(parts[0].Trim());       // tea
        Console.WriteLine(string.Join("|", parts));

        // A string never changes in place: each method hands
        // back a new string, and the original is untouched.
    }
}

A label built from text and numbers

Run it and read the lines. Then add a line that shows the price with three decimal places, and one that joins the parts with a comma and a space instead of a bar.

Output
Click "Run" to execute your code

Put the format inside the hole, as in {price:F2}, rather than formatting the whole sentence. One hole, one decision about how that single value is shown.

What a string actually is

A string is a sequence of characters, and Length counts them. name[0] reads the first character and hands back a char, not a one-character string. That distinction matters the moment you compare the result with "A" instead of 'A', because a char and a string are different types.

The characters are stored as UTF-16 code units, so most letters take one slot and some symbols take two. That is why Length can differ from the number of glyphs a reader sees. For ordinary names and labels the two agree, and the distinction is worth knowing rather than worrying about.

Comparing strings with == is ordinal by default: "Ada" and "ada" are different values. When case should not matter, say so with ToUpper or ToLower on both sides, or use a comparison that names its rule.

Formatting is shaped by culture

F2 means two digits after the decimal separator, but the separator itself comes from the current culture. On a machine set to a comma-decimal culture, the same value prints as 12,50. The number is unchanged; the text around it is not.

That is why text made for a person should not be parsed back into a number without saying which culture wrote it. Keep the value as a number until the last moment, format it once, and treat the result as display only.

Three everyday slips: dropping the $ leaves the braces visible in the output; "Total: " + 1 + 2 joins from left to right and prints Total: 12; and Substring(0, 3) asks for three characters, so a shorter string throws rather than returning what it has.

Try these next

  • Print a name with ToUpper and with Substring, then print the original again to confirm it did not change.
  • Build one line with + and the same line with $"...", and compare how easy each is to read.
  • Split "red, green, blue" on the comma, trim each part, and join them back with a slash.

A receipt that lines up

Run it and read the four rows. Then add a third item and keep the numbers in the same columns by using the same alignment values.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What does the dollar sign do in $"Total: {price}"?