String is one of the most commonly used types. Interpolation string is supported from C#6 to facilitate string operation. Most analyzers also recommend interpolation because it makes our code clearer and simpler. C#10 in. NET6 provides us with a better implementation and better performance. So what is an interpolation string? It starts with a character string, similar to "Hello {name}". The following example is a simple use of interpolation string:
var name = "Interpolation string"; var hello = $"Hello {name}!"; var num= 10; var numMessage= $"I like numbers {num}";
We can directly simplify string splicing without using format, and some simple string splicing can be simplified into string.Concat. In versions before. NET6, it will be translated into string.Format in lower version C# and the above code will be translated into lower version C# code as follows:
string name = "Interpolation string"; string hello = string.Concat("Hello ", name, "!"); int num= 10; string numMessage= string.Format("I like numbers {0}", );
For string.Format, if the parameter is a value type, it will be boxed and become an object, which can be seen from the IL code. It should be noted that the current CultureInfo will be used when formatting the interpolation string. If we need to use different CultureInfo or manually specify CultureInfo, we can use FormattableString or FormattableStringFactory. The code is as follows. Different number formats will be displayed according to the specified CultureInfo:
var id=35000; FormattableString str1 = $"id yes{id}"; Console.WriteLine(str1.Format); Console.WriteLine(str1.ToString(new CultureInfo("zh-CN"))); str1 = FormattableStringFactory.Create("Hello {0}", id); Console.WriteLine(str1.Format); Console.WriteLine(str1.ToString(new CultureInfo("en-US")));
In. Net 6, the first code of this article will be translated into the following:
string name = "Interpolation string"; string hello = string.Concat ("Hello ", name, "!"); int num= 10; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(11, 1); defaultInterpolatedStringHandler.AppendLiteral("I like numbers "); defaultInterpolatedStringHandler.AppendFormatted(num); string numDesc = defaultInterpolatedStringHandler.ToStringAndClear();
In. Net 6, interpolated strings are handled by DefaultInterpolatedStringHandler. It DefaultInterpolatedStringHandler is a struct and contains the generic method AppendFormatted to avoid boxing operations, so that it performs better in format. In addition, two methods are added to String in. NET6 to support the use of new interpolation processing methods. The new method code is as follows:
/// Creates a new string by using the specified provider to control the formatting of the specified interpolated string. /// An object that supplies culture-specific formatting information. /// The interpolated string. /// The string that results for formatting the interpolated string using the specified format provider. public static string Create(IFormatProvider? provider, [InterpolatedStringHandlerArgument("provider")] ref DefaultInterpolatedStringHandler handler) => handler.ToStringAndClear(); /// Creates a new string by using the specified provider to control the formatting of the specified interpolated string. /// An object that supplies culture-specific formatting information. /// The initial buffer that may be used as temporary space as part of the formatting operation. The contents of this buffer may be overwritten. /// The interpolated string. /// The string that results for formatting the interpolated string using the specified format provider. public static string Create(IFormatProvider? provider, Span<char> initialBuffer, [InterpolatedStringHandlerArgument("provider", "initialBuffer")] ref DefaultInterpolatedStringHandler handler) => handler.ToStringAndClear();
Let's implement a simple interpolation string processor. To implement the most basic interpolation string processor, we need to meet the following four conditions:
- The constructor requires at least two int parameters, one is the length of constant characters in the string, and the other is the number of parameters to be formatted;
- The AppendLiteral(string s) method with public is required to handle the splicing of constant characters;
- The appendformatted (T) method with public is required to process parameters;
- The custom processor needs to use the InterpolatedStringHandler tag, and the processor can be class or struct. The following code implements a simple interpolation string processor
[InterpolatedStringHandler] public struct CustomInterpolatedStringHandler { private readonly StringBuilder builder; public CustomInterpolatedStringHandler(int literalLength, int formattedCount) { builder = new StringBuilder(literalLength); } public void AppendLiteral(string s) { builder.Append(s); } public void AppendFormatted<T>(T t) { builder.Append(t?.ToString()); } public override string ToString() { return builder.ToString(); } }
When we use it, we can use it as follows:
private static void LogInterpolatedString(string str) { Console.WriteLine(nameof(LogInterpolatedString)); Console.WriteLine(str); } private static void LogInterpolatedString(CustomInterpolatedStringHandler stringHandler) { Console.WriteLine(nameof(LogInterpolatedString)); Console.WriteLine(nameof(CustomInterpolatedStringHandler)); Console.WriteLine(stringHandler.ToString()); } LogInterpolatedString("My favorite number is 10"); int num=20; LogInterpolatedString($"My favorite number is{num}");
The output results are as follows:
LogInterpolatedString My favorite number is 10 LogInterpolatedString CustomInterpolatedStringHandler My favorite number is 20
We can also add custom parameters to the constructor of the custom interpolation string processor, and use InterpolatedStringHandlerArgument to introduce more constructor parameters. Let's modify the CustomInterpolatedStringHandler code above:
[InterpolatedStringHandler] public struct CustomInterpolatedStringHandler { private readonly StringBuilder builder; private readonly int _limit; public CustomInterpolatedStringHandler(int literalLength, int formattedCount) : this(literalLength, formattedCount, 0) { } public CustomInterpolatedStringHandler(int literalLength, int formattedCount, int limit) { builder = new StringBuilder(literalLength); _limit = limit; } public void AppendLiteral(string s) { builder.Append(s); } public void AppendFormatted<T>(T t) { if (t is int n && n < _limit) { return; } builder.Append(t?.ToString()); } public override string ToString() { return builder.ToString(); } }
We modify the calling code:
private static void LogInterpolatedString(int limit, [InterpolatedStringHandlerArgument("limit")] ref CustomInterpolatedStringHandler stringHandler) { Console.WriteLine(nameof(LogInterpolatedString)); Console.WriteLine($"{nameof(CustomInterpolatedStringHandler)} with limit:{limit}"); Console.WriteLine(stringHandler.ToString()); }
In the calling code, we checked that if the parameter is int and less than the passed limit parameter, it will not be spliced. Let's modify the calling code again:
LogInterpolatedString(10, $"My favorite number is{num}"); Console.WriteLine(); LogInterpolatedString(15, $"My favorite number is{num}");
The output results are as follows:
LogInterpolatedString CustomInterpolatedStringHandler with limit:10 My favorite number is 10 LogInterpolatedString CustomInterpolatedStringHandler with limit:15 My favorite number is
From the above output results, it can be seen that num is printed for the first time and num is not printed for the second time. In fact, there is another special parameter. We can introduce a bool type out parameter into the construction method. If the value is false, string splicing will not be carried out. Let's modify the previous code again:
public CustomInterpolatedStringHandler(int literalLength, int formattedCount, int limit, out bool shouldAppend) { shouldAppend = limit < 30; builder = new StringBuilder(shouldAppend ? literalLength : 0); _limit = limit; }
When the limit parameter is less than 30, the string is spliced, otherwise it will not be output. The calling code is modified as follows:
LogInterpolatedString(10, $"My favorite number is {num}"); Console.WriteLine(); LogInterpolatedString(15, $"My favorite number is{num}"); Console.WriteLine(); LogInterpolatedString(30, $"My favorite number is{num}");
The output is like this
LogInterpolatedString CustomInterpolatedStringHandler with limit:10 My favorite number is 10 LogInterpolatedString CustomInterpolatedStringHandler with limit:15 My favorite number is LogInterpolatedString CustomInterpolatedStringHandler with limit:30
You can see that when the limit is 30, the output is an empty line without any content.