-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API Proposal: C# 10 interpolated strings support, part 2 #50635
Labels
Milestone
Comments
stephentoub
added
area-System.Runtime
api-ready-for-review
API is ready for review, it is NOT ready for implementation
labels
Apr 2, 2021
This comment has been minimized.
This comment has been minimized.
stephentoub
added
the
blocking
Marks issues that we want to fast track in order to unblock other important work
label
Apr 5, 2021
This was referenced Apr 9, 2021
We didn't finish this in API Review, here were the notes so far
|
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class InterpolatedBuilderArgumentAttribute : Attribute
{
public InterpolatedBuilderArgumentAttribute(string argument);
public InterpolatedBuilderArgumentAttribute(params string[] arguments);
public string[] Arguments { get; }
}
public ref struct InterpolatedStringBuilder // from https://github.com/dotnet/runtime/issues/50601
{
public static InterpolatedStringBuilder Create(int literalLength, int formattedCount, IFormatProvider? provider); // additional factory
public static InterpolatedStringBuilder Create(int literalLength, int formattedCount, IFormatProvider? provider, Span<char> scratchBuffer); // additional factory
…
}
}
namespace System
{
public static class MemoryExtensions
{
public static bool TryWrite(this Span<char> span, [InterpolatedBuilderArgument("span")] ref InterpolatedSpanBuilder builder, out int charsWritten);
public static bool TryWrite(this Span<char> span, IFormatProvider? provider, [InterpolatedBuilderArgument("span", "provider")] ref InterpolatedSpanBuilder builder, out int charsWritten);
…
[EditorBrowsable(Never)]
public ref struct InterpolatedTryWriteBuilder
{
public static InterpolatedTryWriteBuilder Create(int literalLength, int formattedCount, Span<char> destination, out bool success);
public static InterpolatedTryWriteBuilder Create(int literalLength, int formattedCount, Span<char> destination, IFormatProvider? provider, out bool success);
// Same members as on InterpolatedStringBuilder
public bool AppendLiteral(string s);
public bool AppendFormatted<T>(T value);
public bool AppendFormatted<T>(T value, string? format);
public bool AppendFormatted<T>(T value, int alignment);
public bool AppendFormatted<T>(T value, int alignment, string? format);
public bool AppendFormatted(ReadOnlySpan<char> value);
public bool AppendFormatted(ReadOnlySpan<char> value, int alignment = 0, string? format = null);
public bool AppendFormatted(string? value);
public bool AppendFormatted(string? value, int alignment = 0, string? format = null);
public bool AppendFormatted(object? value, int alignment = 0, string? format = null);
}
}
public partial sealed class String
{
public static string Create(IFormatProvider? provider, Span<char> scratchBuffer, [InterpolatedBuilderArgument("provider", "scatchBuffer")] ref InterpolatedStringBuilder builder);
public static string Create(IFormatProvider? provider, [InterpolatedBuilderArgument("provider")] ref InterpolatedStringBuilder builder);
}
}
namespace System.Text
{
public sealed class StringBuilder
{
public StringBuilder Append([InterpolatedBuilderArgument("this")] ref InterpolatedAppendFormatBuilder builder);
public StringBuilder Append(IFormatProvider? provider, [InterpolatedBuilderArgument("this", "provider")] ref InterpolatedAppendFormatBuilder builder);
public StringBuilder AppendLine([InterpolatedBuilderArgument("this")] ref InterpolatedAppendFormatBuilder builder);
public StringBuilder AppendLine(IFormatProvider? provider, [InterpolatedBuilderArgument("this", "provider")] ref InterpolatedAppendFormatBuilder builder);
[EditorBrowsable(Never)]
public struct InterpolatedAppendFormatBuilder
{
public static InterpolatedAppendFormatBuilder Create(int literalLength, int formattedCount, StringBuilder stringBuilder);
public static InterpolatedAppendFormatBuilder Create(int literalLength, int formattedCount, StringBuilder stringBuilder, IFormatProvider? provider);
// Same members as on InterpolatedStringBuilder
public bool AppendLiteral(string s);
public bool AppendFormatted<T>(T value);
public bool AppendFormatted<T>(T value, string? format);
public bool AppendFormatted<T>(T value, int alignment);
public bool AppendFormatted<T>(T value, int alignment, string? format);
public bool AppendFormatted(ReadOnlySpan<char> value);
public bool AppendFormatted(ReadOnlySpan<char> value, int alignment = 0, string? format = null);
public bool AppendFormatted(string? value);
public bool AppendFormatted(string? value, int alignment = 0, string? format = null);
public bool AppendFormatted(object? value, int alignment = 0, string? format = null);
}
…
}
} |
bartonjs
added
api-approved
API was approved in API review, it can be implemented
and removed
api-ready-for-review
API is ready for review, it is NOT ready for implementation
blocking
Marks issues that we want to fast track in order to unblock other important work
labels
Apr 20, 2021
ghost
added
the
in-pr
There is an active PR which will close this issue when it is merged
label
Apr 21, 2021
ghost
removed
the
in-pr
There is an active PR which will close this issue when it is merged
label
Jul 13, 2021
ghost
locked as resolved and limited conversation to collaborators
Aug 12, 2021
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
Background and Motivation
Part 1 covers the APIs underlying interpolated strings when building strings directly.
But the C# 10 support, as outlined in https://github.com/dotnet/csharplang/blob/main/proposals/improved-interpolated-strings.md#improved-interpolated-strings, enables the compiler to target a builder pattern, and we can use that builder pattern to enable additional scenarios beyond just
string s = $"..."
.There are multiple parts to this proposal, each of which stands independently and enables a different but related scenario (with the exception of the first section, an attribute that will be used by the rest).
Proposed API: Attribute for Passing State Into Builder Create Methods
TBD: There will be some attribute that can be placed on one or more method arguments to signal to the compiler it should forward those into the builder’s Create method. See usage in later sections, but its design is still underway.
Proposed API: Writing into Destination Spans
It is becoming increasingly common to format into spans, which enables formatting into stack-allocated memory, into existing buffers code may have, into pooled arrays, and so on. Such formatting today for composite operations involves manually tracking how many characters have been written, slicing the target buffer, and doing length checks for each component. With interpolated strings, we can push all of that handling to the compiler. For example, consider a type like:
It can implement ISpanFormattable like:
We can instead enable a developer to write:
by exposing the following API:
For the previously cited example, the compiler will generate code akin to:
Issues for discussion:
Proposed API: String Formatting With a Provider
#50601 covered basic support for string interpolation using the new builder support. But it didn’t cover customizing that to support custom IFormatProviders. We do that by exposing a new string API and augmenting the builder proposed in the previous issue:
This enables a developer to write code like:
which the compiler will translate into approximately:
Proposed API: String Formatting With Stack Allocation
The above made it possible for a developer to pass in an IFormatProvider. However, for best efficiency, we want to enable a developer to pass in additional state: a scratch buffer that the formatting can use as part of its operation. Often this scratch space will be stackalloc’d space, which is often enough to handle a significant number of formatting operations.
This enables a developer to write code like:
which the compiler will translate into approximately:
The method makes no guarantees about using the scratch buffer, but it’s allowed to use it however it wants for the duration of the operation. If the buffer is not large enough for the formatting, the builder will grow to using ArrayPool buffers as it otherwise would if no scratch buffer was provided.
Proposed API: Appending to a StringBuilder
StringBuilder today has multiple AppendFormat overloads today that take an optional provider, the format string, and arguments. Just as with string formatting, we can provide an AppendFormat overload that accepts a builder, to make this a bit more efficient while also allowing the more convenient syntax. The builder’s TryFormat methods will delegate to corresponding functionality on the StringBuilder.
This enables a developer to write code like:
which the compiler will translate into approximately:
Issues for discussion:
sb.Append($"...");
. If we were to add the AppendFormat functionality instead (or also) under the Append name, those existing uses would get a lot better automatically upon recompilation.The text was updated successfully, but these errors were encountered: