Skip to content
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

The ToString() method of the StringBuilder class is used in conjunction with $ #47633

Open
yinyunpan opened this issue Mar 15, 2025 · 6 comments
Assignees
Labels
untriaged Request triage from a team member

Comments

@yinyunpan
Copy link

`StringBuilder sb = new StringBuilder();
sb.Append("startPort=SHA&endPort=PEK");
sb.Append($"&token={sb.ToString()}3488354");

Console.WriteLine(sb.ToString());`

一、.net 5.0

Meet expectations:

Image

二、.net6.0/.net7/.net8.0/.net9.0

Not meeting expectations:

Image

Image

Image

Image

三、Modified code

`StringBuilder sb = new StringBuilder();
sb.Append("startPort=SHA&endPort=PEK");
string value = sb.ToString();
sb.Append($"&token={value}3488354");

Console.WriteLine(sb.ToString());`

Meet expectations:

Image

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-NuGet untriaged Request triage from a team member labels Mar 15, 2025
@KalleOlaviNiemitalo
Copy link
Contributor

This happens because the Append(ref System.Text.StringBuilder.AppendInterpolatedStringHandler handler) method was added to StringBuilder. SharpLab shows that this code

using System.Text;
public class C {
    public void M() {
        StringBuilder sb = new StringBuilder();
        sb.Append("startPort=SHA&endPort=PEK");
        sb.Append($"&token={sb.ToString()}3488354");
    }
}

is translated to:

public class C
{
    public void M()
    {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.Append("startPort=SHA&endPort=PEK");
        StringBuilder stringBuilder2 = stringBuilder;
        StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(14, 1, stringBuilder2);
        handler.AppendLiteral("&token=");
        handler.AppendFormatted(stringBuilder.ToString());
        handler.AppendLiteral("3488354");
        stringBuilder2.Append(ref handler);
    }
}

where handler.AppendLiteral("&token="); actually appends to the StringBuilder, and that affects the result of stringBuilder.ToString()) in the subsequent handler.AppendFormatted(stringBuilder.ToString()) call.

Although this behaviour may be surprising, I don't expect that it will be changed.

Copy link
Contributor

Thanks for creating this issue! We believe this issue is related to NuGet tooling, which is maintained by the NuGet team. Thus, we closed this one and encourage you to raise this issue in the NuGet repository instead. Don’t forget to check out NuGet’s contributing guide before submitting an issue!

If you believe this issue was closed out of error, please comment to let us know.

Happy Coding!

@KalleOlaviNiemitalo
Copy link
Contributor

It's not related to NuGet tooling. It's more a C# language or runtime issue.

@baronfel baronfel reopened this Mar 15, 2025
@baronfel
Copy link
Member

Bad bot. I agree that this is a Roslyn issue because it is related to the codegen of a specific language feature.

@KalleOlaviNiemitalo
Copy link
Contributor

KalleOlaviNiemitalo commented Mar 15, 2025

I don’t agree that this is a Roslyn issue. Roslyn implements the interpolated string handler feature as it was designed for C#.

I don’t think this can be fixed by changing C#, either. It is too difficult to detect at compile time whether the content of the interpolated string expression depends on the StringBuilder instance to which it is being appended. You might have stringBuilder.Append($"xx{Fun()}"), where string Fun() reads the StringBuilder reference from a field and calls ToString() or get_Length() on it.

This could be fixed in the .NET Runtime, though. Make StringBuilder.AppendInterpolatedStringHandler keep track of the expanded length of the StringBuilder, and commit that change to the StringBuilder only when StringBuilder.Append(ref StringBuilder.AppendInterpolatedStringHandler) is finally called. That would solve the append-only case, but the change might hurt the performance of StringBuilder even when interpolated strings are not used. If the interpolated string expression mutates the StringBuilder in other ways and those changes must likewise be hidden until committed, the fix becomes even more complex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
untriaged Request triage from a team member
Projects
None yet
Development

No branches or pull requests

4 participants