How it works
A .NET string is indexed in UTF-16 code units. Some Unicode scalar values require a surrogate pair, so one scalar can occupy two string positions. The sample contains A and a smiling face scalar. Its string length is three even though enumerating Unicode scalar values produces two items.
UTF-8 encodes the same text using five bytes: one for A and four for the supplementary scalar. The three measurements answer different questions. A buffer allocation, a wire-format limit, and a user-interface character limit may each need a different unit, so name the unit rather than using an ambiguous variable called length.
EnumerateRunes provides a scalar-oriented view and avoids manually splitting a well-formed surrogate pair. Scalar values still are not always user-perceived characters. A visible grapheme can contain several scalars, such as a base letter followed by a combining mark or a multi-scalar emoji sequence. The correct text boundary depends on the task.
A common bug truncates a string at an arbitrary UTF-16 index and leaves only half of a surrogate pair. Another bug applies a byte limit by counting string positions. Both arise from confusing representations. Validate or truncate at a boundary appropriate to the encoding and display requirements.
Practice by adding a second smiling face and predicting all three counts. Then compare a precomposed accented letter with a letter followed by a combining accent, observing scalar and byte counts without assuming they have identical binary representations. Avoid culture-dependent transformations when the task only needs representation measurements.
Finally, define separate helpers for a byte-budget check and a scalar-count report, making their units explicit in names and tests. This browser-friendly lesson uses deterministic encoding and scalar enumeration; normalization, linguistic sorting, and display grapheme rules are additional policies whose runtime support and intended semantics should be verified separately.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
string text = "A\U0001F600";
Console.WriteLine(text.Length);
int scalars = 0;
foreach (var rune in text.EnumerateRunes()) scalars++;
Console.WriteLine(scalars);
Console.WriteLine(Encoding.UTF8.GetByteCount(text));
Expected output
3 2 5
UTF-16 code units, Unicode scalar values, and encoded bytes are different measurements.