Strings
Intro
string in C# is a reference type (System.String) with immutable contents. That combination is important: assignment copies references, but any text change creates a new string value. Understanding this helps you choose between plain concatenation, interpolation, and StringBuilder in performance-sensitive paths.
Deeper Explanation
Core properties
stringis a sealed reference type.- Strings are immutable in safe managed code.
==andEqualscompare string content, not object identity.
var a = "hello";
var b = a;
b = b + "!";
Console.WriteLine(a); // hello
Console.WriteLine(b); // hello!
b = b + "!" creates a new string object; a remains unchanged.
String interning
String literals are interned by default, so identical literals can share the same instance:
var s1 = "dotnet";
var s2 = "dotnet";
Console.WriteLine(object.ReferenceEquals(s1, s2)); // True
Interning can reduce duplicate literal allocations, but it should not be used blindly for large dynamic text.
StringBuilder
Use StringBuilder when you perform many appends in loops or build large text incrementally.
var sb = new StringBuilder(capacity: 256);
for (var i = 0; i < 5; i++)
{
sb.Append("item-").Append(i).AppendLine();
}
var result = sb.ToString();
Decision rule:
- Use
stringfor small/simple composition and readability. - Use
StringBuilderfor repeated mutations in hot paths.
Pitfalls
- Repeated
+=inside loops allocates many temporary strings because each concatenation creates a new instance, which increases GC pressure and latency spikes under load; preferStringBuilderor pre-sized buffers for hot paths. - Using
ReferenceEqualsfor semantic equality can produce flaky logic because interning may return shared references for literals while runtime-built strings with identical content are different objects; compare with APIs that acceptStringComparison(string.Equals,StartsWith,EndsWith,IndexOf) instead. - Omitting
StringComparisonin comparisons/searches can cause culture-dependent bugs (for example Turkish-I casing behavior) that reproduce only in specific locales; useOrdinal/OrdinalIgnoreCasefor identifiers and protocol values.
Tradeoffs
- Interpolation/concatenation vs
StringBuilder: interpolation is clearer for one-off formatting, whileStringBuilderwins for repeated incremental construction in loops. OrdinalIgnoreCasevs culture-aware comparison: ordinal is faster and stable for technical keys, while culture-aware options are safer for UI-facing natural-language text.- Interning vs normal allocation: interning can reduce duplicate literal memory, but forcing interning for dynamic strings can increase long-lived memory retention and hurt throughput.
Questions
When should you choose
StringBuilder over string?
- Use
StringBuilderfor iterative construction (loops, batched appends, streaming transforms) where many intermediate strings would otherwise be allocated. - Prefer interpolation/concatenation for one-off formatting with a small number of values because readability is usually better.
- In hot paths, benchmark both options and pre-size
StringBuildercapacity to reduce buffer growth and copying.
Why can
ReferenceEquals(a, b) be false even when a == b is true for strings?
==for strings compares content, whileReferenceEqualschecks object identity.- Two strings can contain identical text but be different objects (for example, literal vs runtime-composed value).
- Use
ReferenceEqualsonly for diagnostics/allocation analysis, not for business equality logic.
How should string comparisons be written in production code?
- Always call APIs that accept
StringComparison(string.Equals,StartsWith,EndsWith,IndexOf) instead of culture-implicit overloads. - Use
Ordinal/OrdinalIgnoreCasefor identifiers, protocol values, keys, and security-sensitive comparisons. - Use culture-aware comparison only for user-facing natural-language text where locale behavior is expected.
- Keep the comparison policy explicit and consistent across reads/writes to avoid cross-locale bugs.
Links
- System.String class - API reference and core behavior.
- How to: Use StringBuilder in C# - Official guidance for incremental text construction.
- Best practices for strings in .NET - Performance and correctness recommendations.
- String comparison in .NET - Culture/ordinal comparison rules.
- StringBuilder performance best practices (Meziantou) - Practical optimization patterns and pitfalls.