C# string interpolation lets you embed expressions inside string literals with the $"..." syntax. Starting in .NET 6, custom string interpolation with InterpolatedStringHandler takes this further by letting you control how interpolated strings are built, validated, and formatted before they become ordinary text. This guide covers both basic and custom C# string interpolation with working code examples.

Custom String Interpolation in C#

Custom string interpolation in C# lets you control how interpolated strings are built, validated, and formatted before they become ordinary text. If you are trying to understand C# string interpolation beyond the basic $"..." syntax, the key feature to learn is InterpolatedStringHandler.

Starting in .NET 6, InterpolatedStringHandler makes it possible to write custom handlers that can reduce allocations, avoid unnecessary work, and tailor string formatting to your own APIs. In this article, I will walk through what standard string interpolation does, how custom string interpolation changes that behavior, and where it can be useful in real .NET code.

Basic String Interpolation

At its core, string interpolation is a way to embed expressions inside string literals. For example, in C#, you might use string interpolation to format a string like this:

string name = "Kevin";
int age = 39;

string message = $"Hello, my name is {name} and I'm {age} years old.";

This creates a string message that contains the values of name and age inserted into the string literal.

While the built-in string interpolation in C# is certainly useful, it’s also limited. What if you want to format strings in a way that doesn’t make sense for the built-in string interpolation? What if you want to format strings in a way that’s specific to your application or domain?

Custom Interpolation

This is where custom string interpolation comes in. With custom string interpolation, you can create your own string interpolation behavior. Here’s an example of how you might use custom string interpolation in C#:


using System.Runtime.CompilerServices;
using System.Text;

SimpleConsoleLogger.Log($"Interpolated {1} 2nd part {2} 3rd part {DateTime.Now}");

[InterpolatedStringHandler]
public ref struct MessageInterpolatedStringHandler
{
    private readonly StringBuilder _messageStringBuilder;
    private int _count = 0;

    public MessageInterpolatedStringHandler(int literalLength, int formattedCount)
    {
        _messageStringBuilder = new StringBuilder(literalLength);
    }

    public void AppendLiteral(string s)
    {
        _messageStringBuilder.Append(s);
    }

    public void AppendFormatted<T>(T t)
    {
        _count++;
        _messageStringBuilder.Append(t);
    }

    public string BuildMessage() =>
        _messageStringBuilder.ToString();
}

public static class SimpleConsoleLogger
{
    public static void Log(string message)
    {
        Console.WriteLine(message);
    }

    public static void Log(MessageInterpolatedStringHandler handler)
    {
        Console.WriteLine(handler.BuildMessage());
    }
}

In this example, if you were to call the SimpleConsoleLogger Log method with a non-interpolated string, the first function would be called. If you passed an interpolated string, the second method would be called instead. The custom string handler can be altered to provide custom behavior when a literal or formatted value is used in the string.

Video

Here is a video I made introducing the concept with examples:

Conclusion

Custom string interpolation in .NET 6 is a must-have feature for anyone looking to format strings in a way that’s specific to their application or domain. With the InterpolatedStringHandler attribute and the IFormatProvider and ICustomFormatter interfaces, you can create your own custom string interpolation behavior.

If you are looking at related modern C# features, UTF-8 String Literals in C# 11: How u8 Literals Work and Say Hello to the Power of Generic Attributes in C# 11 are good follow-on reads.