Code Inspection: Pass string interpolation expression
One of the biggest downsides of the string interpolation expression feature introduced in C# 6.0 was the inability to efficiently delay string formatting and delegate it to the consumer of the interpolated string expression. This is important in logging frameworks that allow changing the logging level at runtime, where you don't want to waste resources on frequent logging invocations when logging is disabled:
// works fast if the VERBOSE level is disabled
Logger.Verbose("info = {0}", objectWithSlowToString);
// always invokes 'objectWithSlowToString.ToString()', slow
Logger.Verbose($"info = {objectWithSlowToString}");
C# 10 fixes this problem by introducing the concept of interpolated string handlers. For the end-user, this means that a string interpolation expression generally works faster in .NET 6 and allows usage of Span<char>
values in interpolation holes. For library authors, this feature allows controlling whether the string interpolation expression is converted to string or not.
ReSharper recognizes the "interpolated string handlers' pattern in library code and suggests using string interpolation expressions where possible:
public static class LoggerTest
{
public static void Test(String[] args)
{
// Pass string interpolation expression
Logger.Log("length = {0}", args.Length);
}
}
public static class LoggerTest
{
public static void Test(String[] args)
{
Logger.Log($"length = {args.Length}");
}
}
public static class Logger
{
[StringFormatMethod("format")]
public static void Log(string format, params object[] args)
{
// write to file
}
public static void Log(ref CustomInterpolatedStringHandler handler)
{
// write to file
}
[InterpolatedStringHandler]
public readonly struct CustomInterpolatedStringHandler
{
private readonly StringBuilder? _builder;
public CustomInterpolatedStringHandler(int literalLength, int formattedCount, out bool shouldAppend)
{
_builder = new StringBuilder(capacity: literalLength + formattedCount * 11);
shouldAppend = true;
}
public void AppendLiteral(string value) => _builder?.Append(value);
public void AppendFormatted<T>(T value) => _builder?.Append(value);
public void AppendFormatted(string? value) => _builder?.Append(value);
public override string? ToString() => _builder?.ToString();
}
}
Last modified: 07 April 2022