namespace System.Runtime.CompilerServices
{
- /// <summary>Provides a builder used by the language compiler to process interpolated strings into <see cref="string"/> instances.</summary>
- public ref struct InterpolatedStringBuilder
+ /// <summary>Provides a handler used by the language compiler to process interpolated strings into <see cref="string"/> instances.</summary>
+ [InterpolatedStringHandler]
+ public ref struct DefaultInterpolatedStringHandler
{
// Implementation note:
// As this type lives in CompilerServices and is only intended to be targeted by the compiler,
// public APIs eschew argument validation logic in a variety of places, e.g. allowing a null input
// when one isn't expected to produce a NullReferenceException rather than an ArgumentNullException.
- /// <summary>Expected average length of formatted data used for an individual hole.</summary>
+ /// <summary>Expected average length of formatted data used for an individual interpolation expression result.</summary>
/// <remarks>
/// This is inherited from string.Format, and could be changed based on further data.
/// string.Format actually uses `format.Length + args.Length * 8`, but format.Length
- /// includes the holes themselves, e.g. "{0}", and since it's rare to have double-digit
- /// numbers of holes, we bump the 8 up to 11 to account for the three extra characters in "{d}",
+ /// includes the format items themselves, e.g. "{0}", and since it's rare to have double-digit
+ /// numbers of items, we bump the 8 up to 11 to account for the three extra characters in "{d}",
/// since the compiler-provided base length won't include the equivalent character count.
/// </remarks>
private const int GuessedLengthPerHole = 11;
/// <remarks>
/// Custom formatters are very rare. We want to support them, but it's ok if we make them more expensive
/// in order to make them as pay-for-play as possible. So, we avoid adding another reference type field
- /// to reduce the size of the builder and to reduce required zero'ing, by only storing whether the provider
+ /// to reduce the size of the handler and to reduce required zero'ing, by only storing whether the provider
/// provides a formatter, rather than actually storing the formatter. This in turn means, if there is a
/// formatter, we pay for the extra interface call on each AppendFormatted that needs it.
/// </remarks>
private readonly bool _hasCustomFormatter;
- /// <summary>Initializes the builder.</summary>
- /// <param name="initialCapacity">Approximated capacity required to support the interpolated string. The final size may be smaller or larger.</param>
- private InterpolatedStringBuilder(int initialCapacity)
+ /// <summary>Creates a handler used to translate an interpolated string into a <see cref="string"/>.</summary>
+ /// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
+ /// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
+ /// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
+ public DefaultInterpolatedStringHandler(int literalLength, int formattedCount)
{
_provider = null;
- _chars = _arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity);
+ _chars = _arrayToReturnToPool = ArrayPool<char>.Shared.Rent(GetDefaultLength(literalLength, formattedCount));
_pos = 0;
_hasCustomFormatter = false;
}
- /// <summary>Initializes the builder.</summary>
- /// <param name="scratchBuffer">A buffer temporarily transferred to the builder for use as part of its formatting. Contents may be overwritten.</param>
- private InterpolatedStringBuilder(Span<char> scratchBuffer)
- {
- _provider = null;
- _arrayToReturnToPool = null;
- _chars = scratchBuffer;
- _pos = 0;
- _hasCustomFormatter = false;
- }
-
- /// <summary>Initializes the builder.</summary>
- /// <param name="initialCapacity">Approximated capacity required to support the interpolated string. The final size may be smaller or larger.</param>
+ /// <summary>Creates a handler used to translate an interpolated string into a <see cref="string"/>.</summary>
+ /// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
+ /// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
- private InterpolatedStringBuilder(int initialCapacity, IFormatProvider? provider)
+ /// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
+ public DefaultInterpolatedStringHandler(int literalLength, int formattedCount, IFormatProvider? provider)
{
_provider = provider;
- _chars = _arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity);
+ _chars = _arrayToReturnToPool = ArrayPool<char>.Shared.Rent(GetDefaultLength(literalLength, formattedCount));
_pos = 0;
_hasCustomFormatter = provider is not null && HasCustomFormatter(provider);
}
- /// <summary>Initializes the builder.</summary>
- /// <param name="scratchBuffer">A buffer temporarily transferred to the builder for use as part of its formatting. Contents may be overwritten.</param>
+ /// <summary>Creates a handler used to translate an interpolated string into a <see cref="string"/>.</summary>
+ /// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
+ /// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
- private InterpolatedStringBuilder(Span<char> scratchBuffer, IFormatProvider? provider)
+ /// <param name="initialBuffer">A buffer temporarily transferred to the handler for use as part of its formatting. Contents may be overwritten.</param>
+ /// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
+ public DefaultInterpolatedStringHandler(int literalLength, int formattedCount, IFormatProvider? provider, Span<char> initialBuffer)
{
_provider = provider;
+ _chars = initialBuffer;
_arrayToReturnToPool = null;
- _chars = scratchBuffer;
_pos = 0;
_hasCustomFormatter = provider is not null && HasCustomFormatter(provider);
}
- /// <summary>Creates a builder used to translate an interpolated string into a <see cref="string"/>.</summary>
- /// <param name="literalLength">The number of constant characters outside of holes in the interpolated string.</param>
- /// <param name="formattedCount">The number of holes in the interpolated string.</param>
- /// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
- public static InterpolatedStringBuilder Create(int literalLength, int formattedCount) =>
- new InterpolatedStringBuilder(GetDefaultLength(literalLength, formattedCount));
-
- /// <summary>Creates a builder used to translate an interpolated string into a <see cref="string"/>.</summary>
- /// <param name="literalLength">The number of constant characters outside of holes in the interpolated string.</param>
- /// <param name="formattedCount">The number of holes in the interpolated string.</param>
- /// <param name="provider">An object that supplies culture-specific formatting information.</param>
- /// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
- public static InterpolatedStringBuilder Create(int literalLength, int formattedCount, IFormatProvider? provider) =>
- new InterpolatedStringBuilder(GetDefaultLength(literalLength, formattedCount), provider);
-
- /// <summary>Creates a builder used to translate an interpolated string into a <see cref="string"/>.</summary>
- /// <param name="literalLength">The number of constant characters outside of holes in the interpolated string.</param>
- /// <param name="formattedCount">The number of holes in the interpolated string.</param>
- /// <param name="scratchBuffer">A buffer temporarily transferred to the builder for use as part of its formatting. Contents may be overwritten.</param>
- /// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
- public static InterpolatedStringBuilder Create(int literalLength, int formattedCount, Span<char> scratchBuffer) =>
- new InterpolatedStringBuilder(scratchBuffer);
-
- /// <summary>Creates a builder used to translate an interpolated string into a <see cref="string"/>.</summary>
- /// <param name="literalLength">The number of constant characters outside of holes in the interpolated string.</param>
- /// <param name="formattedCount">The number of holes in the interpolated string.</param>
- /// <param name="provider">An object that supplies culture-specific formatting information.</param>
- /// <param name="scratchBuffer">A buffer temporarily transferred to the builder for use as part of its formatting. Contents may be overwritten.</param>
- /// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
- public static InterpolatedStringBuilder Create(int literalLength, int formattedCount, IFormatProvider? provider, Span<char> scratchBuffer) =>
- new InterpolatedStringBuilder(scratchBuffer, provider);
-
- /// <summary>Derives a default length with which to seed the builder.</summary>
- /// <param name="literalLength">The number of constant characters outside of holes in the interpolated string.</param>
- /// <param name="formattedCount">The number of holes in the interpolated string.</param>
+ /// <summary>Derives a default length with which to seed the handler.</summary>
+ /// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
+ /// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] // becomes a constant when inputs are constant
private static int GetDefaultLength(int literalLength, int formattedCount) =>
Math.Max(MinimumArrayPoolLength, literalLength + (formattedCount * GuessedLengthPerHole));
/// <summary>Gets the built <see cref="string"/>.</summary>
/// <returns>The built string.</returns>
- public override string ToString() => new string(_chars.Slice(0, _pos));
+ public override string ToString() => new string(Text);
- /// <summary>Gets the built <see cref="string"/> and clears the builder.</summary>
+ /// <summary>Gets the built <see cref="string"/> and clears the handler.</summary>
/// <returns>The built string.</returns>
/// <remarks>
- /// This releases any resources used by the builder. The method should be invoked only
- /// once and as the last thing performed on the builder. Subsequent use is erroneous, ill-defined,
- /// and may destabilize the process, as may using any other copies of the builder after ToStringAndClear
+ /// This releases any resources used by the handler. The method should be invoked only
+ /// once and as the last thing performed on the handler. Subsequent use is erroneous, ill-defined,
+ /// and may destabilize the process, as may using any other copies of the handler after ToStringAndClear
/// is called on any one of them.
/// </remarks>
public string ToStringAndClear()
{
- string result = new string(_chars.Slice(0, _pos));
+ string result = new string(Text);
+ Clear();
+ return result;
+ }
+ /// <summary>Clears the handler, returning any rented array to the pool.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] // used only on a few hot paths
+ internal void Clear()
+ {
char[]? toReturn = _arrayToReturnToPool;
this = default; // defensive clear
if (toReturn is not null)
{
ArrayPool<char>.Shared.Return(toReturn);
}
-
- return result;
}
- /// <summary>Writes the specified string to the builder.</summary>
+ /// <summary>Gets a span of the written characters thus far.</summary>
+ internal ReadOnlySpan<char> Text => _chars.Slice(0, _pos);
+
+ /// <summary>Writes the specified string to the handler.</summary>
/// <param name="value">The string to write.</param>
public void AppendLiteral(string value)
{
#region AppendFormatted
// Design note:
- // The compiler requires a AppendFormatted overload for anything that might be within a hole;
- // if it can't find an appropriate overload, for builders in general it'll simply fail to compile.
- // (For target-typing to string where it uses InterpolatedStringBuilder implicitly, it'll instead fall back to
+ // The compiler requires a AppendFormatted overload for anything that might be within an interpolation expression;
+ // if it can't find an appropriate overload, for handlers in general it'll simply fail to compile.
+ // (For target-typing to string where it uses DefaultInterpolatedStringHandler implicitly, it'll instead fall back to
// its other mechanisms, e.g. using string.Format. This fallback has the benefit that if we miss a case,
// interpolated strings will still work, but it has the downside that a developer generally won't know
// if the fallback is happening and they're paying more.)
// (object value, int alignment = 0, string? format = null)
// Such an overload would provide the same expressiveness as string.Format. However, this has several
// shortcomings:
- // - Every value type in a hole would be boxed.
- // - ReadOnlySpan<char> could not be used in holes.
+ // - Every value type in an interpolation expression would be boxed.
+ // - ReadOnlySpan<char> could not be used in interpolation expressions.
// - Every AppendFormatted call would have three arguments at the call site, bloating the IL further.
// - Every invocation would be more expensive, due to lack of specialization, every call needing to account
// for alignment and format, etc.
// (ReadOnlySpan<char>, int alignment, string? format)
// but this also has shortcomings:
// - Some expressions that would have worked with an object overload will now force a fallback to string.Format
- // (or fail to compile if the builder is used in places where the fallback isn't provided), because the compiler
+ // (or fail to compile if the handler is used in places where the fallback isn't provided), because the compiler
// can't always target type to T, e.g. `b switch { true => 1, false => null }` where `b` is a bool can successfully
// be passed as an argument of type `object` but not of type `T`.
// - Reference types get no benefit from going through the generic code paths, and actually incur some overheads
// at compile time for value types but don't (currently) if the Nullable<T> goes through the same code paths
// (see https://github.com/dotnet/runtime/issues/50915).
//
- // We could try to take a more elaborate approach for InterpolatedStringBuilder, since it is the most common builder
+ // We could try to take a more elaborate approach for DefaultInterpolatedStringHandler, since it is the most common handler
// and we want to minimize overheads both at runtime and in IL size, e.g. have a complete set of overloads for each of:
// (T, ...) where T : struct
// (T?, ...) where T : struct
// - Any reference type with an implicit cast to ROS<char> will fail to compile due to ambiguities between the overloads. string
// is one such type, hence needing dedicated overloads for it that can be bound to more tightly.
//
- // A middle ground we've settled on, which is likely to be the right approach for most other builders as well, would be the set:
+ // A middle ground we've settled on, which is likely to be the right approach for most other handlers as well, would be the set:
// (T, ...) with no constraint
// (ReadOnlySpan<char>) and (ReadOnlySpan<char>, int)
// (object, int alignment = 0, string? format = null)
// importantly which can't be boxed to be passed to ICustomFormatter.Format.
#region AppendFormatted T
- /// <summary>Writes the specified value to the builder.</summary>
+ /// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
public void AppendFormatted<T>(T value)
{
AppendLiteral(s);
}
}
- /// <summary>Writes the specified value to the builder.</summary>
+ /// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="format">The format string.</param>
public void AppendFormatted<T>(T value, string? format)
}
}
- /// <summary>Writes the specified value to the builder.</summary>
+ /// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
public void AppendFormatted<T>(T value, int alignment)
}
}
- /// <summary>Writes the specified value to the builder.</summary>
+ /// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="format">The format string.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
#endregion
#region AppendFormatted ReadOnlySpan<char>
- /// <summary>Writes the specified character span to the builder.</summary>
+ /// <summary>Writes the specified character span to the handler.</summary>
/// <param name="value">The span to write.</param>
public void AppendFormatted(ReadOnlySpan<char> value)
{
}
}
- /// <summary>Writes the specified string of chars to the builder.</summary>
+ /// <summary>Writes the specified string of chars to the handler.</summary>
/// <param name="value">The span to write.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
/// <param name="format">The format string.</param>
#endregion
#region AppendFormatted string
- /// <summary>Writes the specified value to the builder.</summary>
+ /// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
public void AppendFormatted(string? value)
{
}
}
- /// <summary>Writes the specified value to the builder.</summary>
+ /// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <remarks>
/// Slow path to handle a custom formatter, potentially null value,
}
}
- /// <summary>Writes the specified value to the builder.</summary>
+ /// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
/// <param name="format">The format string.</param>
#endregion
#region AppendFormatted object
- /// <summary>Writes the specified value to the builder.</summary>
+ /// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
/// <param name="format">The format string.</param>
#endregion
/// <summary>Gets whether the provider provides a custom formatter.</summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)] // only used in two hot path call sites
- private static bool HasCustomFormatter(IFormatProvider provider)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] // only used in a few hot path call sites
+ internal static bool HasCustomFormatter(IFormatProvider provider)
{
Debug.Assert(provider is not null);
Debug.Assert(provider is not CultureInfo || provider.GetFormat(typeof(ICustomFormatter)) is null, "Expected CultureInfo to not provide a custom formatter");
}
}
- /// <summary>Handles adding any padding required for aligning a formatted value in a hole.</summary>
+ /// <summary>Handles adding any padding required for aligning a formatted value in an interpolation expression.</summary>
/// <param name="startingPos">The position at which the written value started.</param>
/// <param name="alignment">Non-zero minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
private void AppendOrInsertAlignmentIfNeeded(int startingPos, int alignment)
namespace System.Runtime.CompilerServices.Tests
{
- public class InterpolatedStringBuilderTests
+ public class DefaultInterpolatedStringHandlerTests
{
[Theory]
[InlineData(0, 0)]
[InlineData(-16, 1)]
public void LengthAndHoleArguments_Valid(int literalLength, int formattedCount)
{
- InterpolatedStringBuilder.Create(literalLength, formattedCount);
+ new DefaultInterpolatedStringHandler(literalLength, formattedCount);
Span<char> scratch1 = stackalloc char[1];
foreach (IFormatProvider provider in new IFormatProvider[] { null, new ConcatFormatter(), CultureInfo.InvariantCulture, CultureInfo.CurrentCulture, new CultureInfo("en-US"), new CultureInfo("fr-FR") })
{
- InterpolatedStringBuilder.Create(literalLength, formattedCount, provider);
+ new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider);
- InterpolatedStringBuilder.Create(literalLength, formattedCount, provider, default);
- InterpolatedStringBuilder.Create(literalLength, formattedCount, provider, scratch1);
- InterpolatedStringBuilder.Create(literalLength, formattedCount, provider, Array.Empty<char>());
- InterpolatedStringBuilder.Create(literalLength, formattedCount, provider, new char[256]);
+ new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider, default);
+ new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider, scratch1);
+ new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider, Array.Empty<char>());
+ new DefaultInterpolatedStringHandler(literalLength, formattedCount, provider, new char[256]);
}
-
- InterpolatedStringBuilder.Create(literalLength, formattedCount, Span<char>.Empty);
- InterpolatedStringBuilder.Create(literalLength, formattedCount, scratch1);
- InterpolatedStringBuilder.Create(literalLength, formattedCount, Array.Empty<char>());
- InterpolatedStringBuilder.Create(literalLength, formattedCount, new char[256]);
}
[Fact]
public void ToString_DoesntClear()
{
- InterpolatedStringBuilder builder = InterpolatedStringBuilder.Create(0, 0);
- builder.AppendLiteral("hi");
+ DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(0, 0);
+ handler.AppendLiteral("hi");
for (int i = 0; i < 3; i++)
{
- Assert.Equal("hi", builder.ToString());
+ Assert.Equal("hi", handler.ToString());
}
- Assert.Equal("hi", builder.ToStringAndClear());
+ Assert.Equal("hi", handler.ToStringAndClear());
}
[Fact]
public void ToStringAndClear_Clears()
{
- InterpolatedStringBuilder builder = InterpolatedStringBuilder.Create(0, 0);
- builder.AppendLiteral("hi");
- Assert.Equal("hi", builder.ToStringAndClear());
- Assert.Equal(string.Empty, builder.ToStringAndClear());
+ DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(0, 0);
+ handler.AppendLiteral("hi");
+ Assert.Equal("hi", handler.ToStringAndClear());
+ Assert.Equal(string.Empty, handler.ToStringAndClear());
}
[Fact]
public void AppendLiteral()
{
var expected = new StringBuilder();
- InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
+ DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);
foreach (string s in new[] { "", "a", "bc", "def", "this is a long string", "!" })
{
public void AppendFormatted_ReadOnlySpanChar()
{
var expected = new StringBuilder();
- InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
+ DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);
foreach (string s in new[] { "", "a", "bc", "def", "this is a longer string", "!" })
{
public void AppendFormatted_String()
{
var expected = new StringBuilder();
- InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
+ DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);
foreach (string s in new[] { null, "", "a", "bc", "def", "this is a longer string", "!" })
{
var provider = new ConcatFormatter();
var expected = new StringBuilder();
- InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0, provider);
+ DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0, provider);
foreach (string s in new[] { null, "", "a" })
{
public void AppendFormatted_ReferenceTypes()
{
var expected = new StringBuilder();
- InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
+ DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);
foreach (string rawInput in new[] { null, "", "a", "bc", "def", "this is a longer string", "!" })
{
public void AppendFormatted_ReferenceTypes_CreateProviderFlowed(bool useScratch)
{
var provider = new CultureInfo("en-US");
- InterpolatedStringBuilder builder = useScratch ?
- InterpolatedStringBuilder.Create(1, 2, provider, stackalloc char[16]) :
- InterpolatedStringBuilder.Create(1, 2, provider);
+ DefaultInterpolatedStringHandler handler = useScratch ?
+ new DefaultInterpolatedStringHandler(1, 2, provider, stackalloc char[16]) :
+ new DefaultInterpolatedStringHandler(1, 2, provider);
foreach (IHasToStringState tss in new IHasToStringState[] { new FormattableStringWrapper("hello"), new SpanFormattableStringWrapper("hello") })
{
- builder.AppendFormatted(tss);
+ handler.AppendFormatted(tss);
Assert.Same(provider, tss.ToStringState.LastProvider);
- builder.AppendFormatted(tss, 1);
+ handler.AppendFormatted(tss, 1);
Assert.Same(provider, tss.ToStringState.LastProvider);
- builder.AppendFormatted(tss, "X2");
+ handler.AppendFormatted(tss, "X2");
Assert.Same(provider, tss.ToStringState.LastProvider);
- builder.AppendFormatted(tss, 1, "X2");
+ handler.AppendFormatted(tss, 1, "X2");
Assert.Same(provider, tss.ToStringState.LastProvider);
}
}
var provider = new ConcatFormatter();
var expected = new StringBuilder();
- InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0, provider);
+ DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0, provider);
foreach (string s in new[] { null, "", "a" })
{
void Test<T>(T t)
{
var expected = new StringBuilder();
- InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0);
+ DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0);
// struct
expected.AppendFormat("{0}", t);
void Test<T>(T t)
{
var provider = new CultureInfo("en-US");
- InterpolatedStringBuilder builder = useScratch ?
- InterpolatedStringBuilder.Create(1, 2, provider, stackalloc char[16]) :
- InterpolatedStringBuilder.Create(1, 2, provider);
+ DefaultInterpolatedStringHandler handler = useScratch ?
+ new DefaultInterpolatedStringHandler(1, 2, provider, stackalloc char[16]) :
+ new DefaultInterpolatedStringHandler(1, 2, provider);
- builder.AppendFormatted(t);
+ handler.AppendFormatted(t);
Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider);
- builder.AppendFormatted(t, 1);
+ handler.AppendFormatted(t, 1);
Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider);
- builder.AppendFormatted(t, "X2");
+ handler.AppendFormatted(t, "X2");
Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider);
- builder.AppendFormatted(t, 1, "X2");
+ handler.AppendFormatted(t, 1, "X2");
Assert.Same(provider, ((IHasToStringState)t).ToStringState.LastProvider);
}
}
var expected = new StringBuilder();
- InterpolatedStringBuilder actual = InterpolatedStringBuilder.Create(0, 0, provider);
+ DefaultInterpolatedStringHandler actual = new DefaultInterpolatedStringHandler(0, 0, provider);
// struct
expected.AppendFormat(provider, "{0}", t);
public void Grow_Large(bool useScratch)
{
var expected = new StringBuilder();
- InterpolatedStringBuilder builder = useScratch ?
- InterpolatedStringBuilder.Create(3, 1000, null, stackalloc char[16]) :
- InterpolatedStringBuilder.Create(3, 1000);
+ DefaultInterpolatedStringHandler handler = useScratch ?
+ new DefaultInterpolatedStringHandler(3, 1000, null, stackalloc char[16]) :
+ new DefaultInterpolatedStringHandler(3, 1000);
for (int i = 0; i < 1000; i++)
{
- builder.AppendFormatted(i);
+ handler.AppendFormatted(i);
expected.Append(i);
- builder.AppendFormatted(i, 3);
+ handler.AppendFormatted(i, 3);
expected.AppendFormat("{0,3}", i);
}
- Assert.Equal(expected.ToString(), builder.ToStringAndClear());
+ Assert.Equal(expected.ToString(), handler.ToStringAndClear());
}
private static void AssertModeMatchesType<T>(T tss) where T : IHasToStringState