C# String Length: Property, Pitfalls, and Performance
Understand the C# string Length property, including UTF-16 code units, null handling, and performance trade-offs.
The Length property is the simplest way to measure a C# string, but it counts UTF-16 code units rather than visible characters. That distinction affects validation, truncation, and any code that works with user-facing text. The examples below show how Length behaves and when to switch to Unicode-aware APIs.
The Length Property and UTF-16 Code Units
In .NET, a string is an immutable sequence of char values, and each char is a 16-bit UTF-16 code unit. The Length property returns the number of code units. For most Latin-based text, one code unit equals one character, but not for supplementary characters or combining sequences. For example, the emoji 😀 is represented as two UTF-16 code units (a surrogate pair), so "😀".Length returns 2, not 1.
string emoji = "😀"; Console.WriteLine(emoji.Length); // Output: 2
Similarly, an accented character can be represented as a base character plus a combining mark, which increases the code unit count. If your application needs to count logical characters or grapheme clusters, Length alone is insufficient.
Getting the Length of a Null String
Calling Length on a null string throws a NullReferenceException. This is a common source of runtime errors when strings come from external input or optional parameters. Guard against it with the null-conditional operator or an explicit check.
string? maybeNull = GetStringFromDatabase(); int len = maybeNull?.Length ?? 0; // 0 if null
Alternatively, use string.IsNullOrEmpty when you only care about whether a string is non-empty. If you need the actual length of a non-null string, the null-conditional pattern is direct and safe.
Counting Characters vs. Code Points
To count Unicode code points (which approximate user-perceived characters), use System.Globalization.StringInfo. Its LengthInTextElements property counts grapheme clusters, handling surrogate pairs and combining sequences correctly.
using System.Globalization; string text = "a😀e\u0301"; // 'a', emoji, 'e' + combining acute accent int codeUnitCount = text.Length; // 5 int textElementCount = new StringInfo(text).LengthInTextElements; // 3
For a simpler approach that counts code points rather than full grapheme clusters, use EnumerateRunes:
int codePointCount = text.EnumerateRunes().Count();
EnumerateRunes is available in .NET Core 3.0 and later. It returns one Rune per Unicode scalar value, so a surrogate pair is counted once, while combining marks remain separate.
The following table summarizes the differences:
| Method | Counts | Handles surrogate pairs | Handles combining marks |
|---|---|---|---|
string.Length | UTF-16 code units | No | No |
EnumerateRunes().Count() | Unicode code points | Yes | No (combining marks are separate) |
StringInfo.LengthInTextElements | Grapheme clusters | Yes | Yes |
Performance: Length Is O(1) and Cached
The Length property is backed by a field in the string object, so accessing it is an O(1) operation. There is no iteration or calculation. This makes it safe to use repeatedly in loops or comparisons without performance concerns.
However, be careful about creating new strings just to check their length. For example, Trim().Length allocates a new string and then reads its length. If you only need to know whether the trimmed string is empty, use string.IsNullOrWhiteSpace instead.
// Avoid: allocates a trimmed string if (input.Trim().Length == 0) { ... } // Prefer: no allocation if (string.IsNullOrWhiteSpace(input)) { ... }
Similarly, avoid using Length to validate that a string has a minimum number of characters when you actually need to count Unicode characters. The cost of the property itself is trivial, but the logic around it can introduce allocations or incorrect results.
Common Mistakes and Misconceptions
A frequent mistake is using Length to enforce a maximum number of characters in user input. For example, a text field that allows 140 characters per Twitter's old limit should be checked by counting grapheme clusters, not UTF-16 code units. Otherwise, users with emoji or accented characters will be unfairly restricted.
Another misconception is that Length returns the number of bytes. That is only true for ASCII strings. For non-ASCII characters, the byte count depends on the encoding (for example, UTF-8 or UTF-16). Use Encoding.UTF8.GetByteCount if you need byte length.
string text = "café"; int charCount = text.Length; // 4 int byteCount = Encoding.UTF8.GetByteCount(text); // 5
Practical Usage: Validation and Truncation
When you need to truncate a string to a certain number of characters, Length is useful only if you are working with code units. For display purposes, you often want to truncate at a code-point boundary to avoid splitting a surrogate pair. Here is a simple method that truncates to a maximum number of code points:
public static string Truncate(string input, int maxCodePoints) { if (string.IsNullOrEmpty(input) || maxCodePoints <= 0) return string.Empty; var builder = new System.Text.StringBuilder(); int count = 0; foreach (System.Text.Rune rune in input.EnumerateRunes()) { if (count >= maxCodePoints) break; builder.Append(rune.ToString()); count++; } return builder.ToString(); }
This uses EnumerateRunes to get Unicode scalar values and rebuilds the string. It avoids splitting a surrogate pair because each rune is a complete code point. For full grapheme clustering, you would need StringInfo and more complex logic, but for most cases code points are sufficient.
When Length Does Not Reflect Display Width
The Length property also does not account for the visual width of characters. For example, East Asian full-width characters take up more horizontal space than Latin characters, but both count as one code unit. If you are building a UI that aligns text, you cannot rely on Length for width calculations. You would need to use font metrics or a library that measures rendered text.
Similarly, combining characters can change visual appearance without matching the code unit count users expect. For instance, "e\u0301" (e + combining acute accent) has a length of 2 but displays as a single character é. If you are counting visible characters, normalize the string or use grapheme clustering.
Understanding these boundaries helps you choose the right tool: Length for code units, EnumerateRunes for code points, and StringInfo for grapheme clusters. Each has its place, and selecting the correct one prevents subtle bugs in internationalized applications.