String interpolation
A receipt is printed from a template. The till has a ready line "Item: ___, quantity: ___", and with every purchase the name and the number go into the blanks. The template itself stays the same; only what goes into it changes.
So far you have built text from values with a plus. It works, but quotes and pluses soon get mixed up, and a space at the edge of a piece often gets lost. Compare two ways of writing the same line:
string item = "Tea";
int qty = 2;
Console.WriteLine("Item: " + item + ", qty: " + qty); // Item: Tea, qty: 2
Console.WriteLine($"Item: {item}, qty: {qty}"); // Item: Tea, qty: 2Both statements print the same thing. Let's take the second one apart:
$before the opening quote tells the compiler that the string has places for values.{item}is such a place: the value of the variableitem, that is,Tea, goes in instead of it.{qty}puts in the number2, turned into text.- Everything else is the ordinary text of the template:
Item:, a comma and spaces.
This notation is called string interpolation, and a string with $ is called an interpolated string. The template reads like the future text, so it is harder to get the spaces wrong.
Expressions in braces
This is where the till's template and interpolation part ways. The blanks of a receipt take ready values, while the braces may hold any expression: arithmetic, a comparison, the length of a string. C# evaluates it and puts in the result. Let's continue the example:
int price = 35;
Console.WriteLine($"Total: {qty * price}"); // Total: 70
Console.WriteLine($"Letters: {item.Length}"); // Letters: 3
Console.WriteLine($"{qty} {(qty > 1 ? "cups" : "cup")}"); // 2 cups
Console.WriteLine($"Sum: {2 + 3}"); // Sum: 5
Console.WriteLine($"{{qty}} = {qty}"); // {qty} = 2Let's go through the lines in order:
{qty * price}multiplies first and then puts in the result70.{item.Length}puts in the length of the string"Tea".- The conditional operator
?:goes in parentheses. Without them, C# reads the colon inside the braces as the start of a format, covered below, and the program does not compile. {2 + 3}givesSum: 5. Remember"Sum: " + 2 + 3from the theme on types? It gaveSum: 23, because the plus joined text from left to right. Inside the braces the expression is evaluated on its own, and2 + 3gives the number5.- To print a brace itself, you double it:
{{and}}.
Width and format
A receipt is printed in columns: names on the left, prices on the right, and the digits stand one under another. For this, after the value inside the braces you give a width and a format.
Width {x,5}
The number after the comma is the width of the place in characters. If the value is shorter, the missing characters are filled with spaces:
- a positive width
{x,5}pushes the value to the right edge, and the spaces go on the left—this is how numbers are aligned; - a negative width
{x,-8}pushes the value to the left edge—this is how names are aligned.
The width is a minimum. A long value is not cut: {12345,3} prints all five digits.
Format {x:F2}
The letters after the colon set the format. F2 prints exactly two fractional digits and rounds the extra ones. The digit after F is the number of digits: F0 prints no fractional part, F1 prints one digit. Prices usually take F2.
Width and format can be combined in one place: {total,8:F2}. The width comes first, then the format.
decimal total = 4.5m;
Console.WriteLine($"|{"Tea",-8}|{70,5}|"); // |Tea | 70|
Console.WriteLine($"|{"Coffee",-8}|{120,5}|"); // |Coffee | 120|
Console.WriteLine($"{total:F2}"); // 4.50
Console.WriteLine($"{2.0 / 3:F2}"); // 0.67
Console.WriteLine($"[{total,8:F2}]"); // [ 4.50]Let's see what we got:
- In the first two lines the widths
-8and5lined the names and prices up in columns. The bars|show the edges of the places. {total:F2}added a zero:4.5turned into4.50.{2.0 / 3:F2}rounded0.666…to0.67.{total,8:F2}first formatted the number and then aligned it to the right within eight characters.
The comments show the output on a computer with English regional settings. With Russian settings the F2 format puts a comma: 4,50 and 0,67. The separator between the whole and the fractional part in the output comes from the computer's settings, while in code it is always a point.
Common mistake
The $ sign before the string was forgotten:
string name = "Anna";
Console.WriteLine("Hi, {name}!"); // ✗warning CS0219: The variable 'name' is assigned but its value is never usedThe program compiles and prints Hi, {name}!. Without $ the braces are ordinary characters, and the variable's name goes into the text as it is. The compiler gives only a warning: the variable name is never used. That is a hint that the string was meant to be interpolated. Add the $, and $"Hi, {name}!" prints Hi, Anna!.
Key points
- In a string with
$, the value of the expression in braces goes in its place:$"qty: {qty}". - The braces may hold any expression, and the conditional operator
?:goes in parentheses. {x,5}sets a width aligned to the right,{x,-5}to the left, and{x:F2}prints two fractional digits.- The fractional separator in the output depends on the regional settings:
4.50or4,50.