dotnet/roslyn

`nameof(X)` in Interpolated String Handlers are not always lowered to constants

Open

#67.295 aperta il 14 mar 2023

Vedi su GitHub
 (9 commenti) (1 reazione) (0 assegnatari)C# (4257 fork)batch import
Area-CompilersConcept-Continuous Improvementhelp wanted

Metriche repository

Star
 (20.414 star)
Metriche merge PR
 (Merge medio 6g 17h) (256 PR mergiate in 30 g)

Descrizione

Version Used: sharplab.io; main (24 Jan 2023)

Steps to Reproduce:

  1. Use an interpolated string with a constant string and a nameof expression.
  2. Observe the whole string lowered to a constant.
  3. Change the interpolated string to add a property.
  4. Observe the nameof expression is now lowered to AppendFormatted (not AppendLiteral)

Source:

public int Property => 2;
public void Test() {
    Console.WriteLine($"abc {nameof(Test)}");
    Console.WriteLine($"abc {nameof(Test)} {Property}");
}

Expected Behavior:

public void Test()
{
    Console.WriteLine("abc Test");
    DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(9, 1);
    defaultInterpolatedStringHandler.AppendLiteral("abc Test "); // <-- merged
    defaultInterpolatedStringHandler.AppendFormatted(Property);
    Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());
}

Actual Behavior:

public void Test()
{
    Console.WriteLine("abc Test");
    DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(5, 2);
    defaultInterpolatedStringHandler.AppendLiteral("abc ");
    defaultInterpolatedStringHandler.AppendFormatted("Test"); // <-- not merged
    defaultInterpolatedStringHandler.AppendLiteral(" ");
    defaultInterpolatedStringHandler.AppendFormatted(Property);
    Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());
}

Note how the nameof expression was combined with the literal string next to it in the first, but not in the second. In addition, the second one lowers the nameof into an AppendFormatted call instead of an AppendLiteral.

Performance

public class InterpolatedBenchmarks
{
#pragma warning disable CA1822
    public int Property => 2;
#pragma warning restore CA1822

    [Benchmark(Baseline = true)]
    public string TestA() =>
        "test TestB " + Property;

    [Benchmark]
    public string TestB() =>
        $"test TestB {Property}";

    [Benchmark]
    public string TestC() =>
        $"test {nameof(TestB)} {Property}";
}

Running the above benchmark (with BenchmarkDotNet) on 7.0.323.6910 (x64 RyuJIT AVX2) shows a slight performance decrease for the actual compiler output compared to what I would expect:

Method Mean Error StdDev Ratio RatioSD
TestA 14.84 ns 0.343 ns 0.321 ns 1.00 0.00
TestB 36.03 ns 0.702 ns 0.656 ns 2.43 0.08
TestC 46.31 ns 0.516 ns 0.458 ns 3.12 0.08

In fact, performance is horrible compared to plain concatenation (lowered to string.Concat).

Guida contributor