String Interpolation vs String.Format

Kry*_*hic 92 c# string resharper performance string-interpolation

使用字符串插值之间是否存在显着的性能差异:

myString += $"{x:x2}";
Run Code Online (Sandbox Code Playgroud)

vs String.Format()?

myString += String.Format("{0:x2}", x);
Run Code Online (Sandbox Code Playgroud)

我只是问,因为Resharper正在提示修复,我之前被愚弄了.

Jer*_*vel 66

值得注意的是相对的.但是:字符串插值string.Format()在编译时转换为最终结果相同.

但是有一些细微的差别:正如我们从这个问题中可以看出的那样,格式说明符中的字符串连接会导致额外的string.Concat()调用.

  • 实际上,在某些情况下(例如,当使用“ int”时),字符串插值可以编译为字符串连接。`var a =“ hello”; var b = $“ {a} world”;`编译为字符串连接。`var a =“ hello”; var b = $“ {a} world {1}”;`编译为字符串格式。 (2认同)

l33*_*33t 21

答案是肯定的和否定的。通过不显示第三个变体ReSharper 愚弄您,这也是性能最高的。列出的两个变体产生相同的 IL 代码,但以下确实会带来提升:

myString += $"{x.ToString("x2")}";
Run Code Online (Sandbox Code Playgroud)

完整的测试代码

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Diagnostics.Windows;
using BenchmarkDotNet.Running;

namespace StringFormatPerformanceTest
{
    [Config(typeof(Config))]
    public class StringTests
    {
        private class Config : ManualConfig
        {
            public Config() => AddDiagnoser(MemoryDiagnoser.Default, new EtwProfiler());
        }

        [Params(42, 1337)]
        public int Data;

        [Benchmark] public string Format() => string.Format("{0:x2}", Data);
        [Benchmark] public string Interpolate() => $"{Data:x2}";
        [Benchmark] public string InterpolateExplicit() => $"{Data.ToString("x2")}";
    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<StringTests>();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

检测结果

|              Method | Data |      Mean |  Gen 0 | Allocated |
|-------------------- |----- |----------:|-------:|----------:|
|              Format |   42 | 118.03 ns | 0.0178 |      56 B |
|         Interpolate |   42 | 118.36 ns | 0.0178 |      56 B |
| InterpolateExplicit |   42 |  37.01 ns | 0.0102 |      32 B |
|              Format | 1337 | 117.46 ns | 0.0176 |      56 B |
|         Interpolate | 1337 | 113.86 ns | 0.0178 |      56 B |
| InterpolateExplicit | 1337 |  38.73 ns | 0.0102 |      32 B |
Run Code Online (Sandbox Code Playgroud)

InterpolateExplicit()方法更快,因为我们现在明确告诉编译器使用string. 无需对要格式化的对象进行装箱。拳击确实是非常昂贵的。另外,请注意我们稍微减少了分配。

  • 不过,如果“x”为“null”,*第三个*变体将会崩溃。 (2认同)
  • 看反编译后的[IL代码sharplab.io/...](https://sharplab.io/#v2:EYLgtghglgdgPgAQMwAIECYUGMCwAoAb3xRLVVgBcUARCCiAbn2NOTQEYAGFAMQHsATpAoAKAJQoAvAD4OnAHT8hdEQCICnEAA90AX1UAaGnQhimeUmTkoAkjAoB TAQAc+AGzoPxU2QBJ1tPTaeqrmlmwIXLb2Tq4ejgCiWs5uUFhQohIyKP4EgRDyACp8AMoUArAA5mo6qmL65rpAA=)。Format 和 Interpolate 的内容完全相同。它使用 string.Format 函数。InterpolateExplicit 中的字符串插值是多余的,它简单地调用 Data.ToString("x2")... 并检查是否为 null。奇怪... Data.ToString("x2") 可以返回 null 吗? (2认同)
  • 我发现有趣的博客文章@meziantou [插值字符串:高级用法](https://www.meziantou.net/interpolated-strings-advanced-usages.htm) (2认同)

Dav*_*ack 16

您应该注意到,C#10 和 .NET 6 中的字符串插值进行了重大优化 - C# 10 和 .NET 6 中的字符串插值

我不仅迁移了字符串格式化的所有用法,还迁移了字符串连接的用法,以使用字符串插值。

我同样担心(如果不是更多的话)不同方法之间的内存分配差异。我发现,当处理少量字符串时,字符串插值几乎总是在速度和内存分配方面获胜。如果您有未确定的(在设计时未知)数量的字符串,则应始终使用System.Text.StringBuilder.Append(xxx)System.Text.StringBuilder.AppendFormat(xxx)

另外,我会指出您+=对字符串连接的用法。要非常小心,并且只对少量小字符串执行此操作。


小智 5

字符串插值在编译时转换为string.Format()。

同样在string.Format中,您可以为单个参数指定多个输出,并为单个参数指定不同的输出格式。但是我猜想字符串插值更易读。因此,取决于您。

a = string.Format("Due date is {0:M/d/yy} at {0:h:mm}", someComplexObject.someObject.someProperty);

b = $"Due date is {someComplexObject.someObject.someProperty:M/d/yy} at {someComplexObject.someObject.someProperty:h:mm}";
Run Code Online (Sandbox Code Playgroud)

有一些性能测试结果https://koukia.ca/string-interpolation-vs-string-format-string-concat-and-string-builder-performance-benchmarks-c1dad38032a

  • 字符串插值 ** 只是有时** 变成了 `String::Format`。有时进入`String::Concat`。恕我直言,该页面上的性能测试并没有真正意义:传递给每个方法的参数数量是相关的。concat 并不总是最快的,stringbuilder 并不总是最慢的。 (2认同)

Zol*_*ási 5

问题是关于性能的,但是标题只是说“vs”,所以我觉得必须补充一些观点,尽管其中一些观点是固执己见的。

  • 本土化

    • 由于其内联代码性质,字符串插值无法本地化。在本地化之前,必须将其转换为string.Format. 然而,有一些工具可以做到这一点(例如ReSharper)。
  • 可维护性(我的意见)

    • string.Format更具可读性,因为它专注于我想要表达的句子,例如在构建漂亮且有意义的错误消息时。使用{N}占位符给了我更大的灵活性,并且以后更容易修改它。
    • 另外,插值中的内联格式说明符很容易被误读,并且在更改时很容易与表达式一起删除。
    • 当使用复杂且长的表达式时,插值很快就会变得更加难以阅读和维护,因此从这个意义上说,当代码不断发展并变得更加复杂时,它无法很好地扩展。string.Format不太容易出现这种情况。
    • 归根结底,这都是关于关注点分离的:我不喜欢将应该如何呈现应该呈现什么混在一起。

因此,基于这些,我决定坚持使用string.Format我的大部分代码。不过,我准备了一个扩展方法,以获得我更喜欢的更流畅的编码方式。该扩展的实现是单行的,在使用中看起来就像这样。

var myErrorMessage = "Value must be less than {0:0.00} for field {1}".FormatWith(maximum, fieldName);
Run Code Online (Sandbox Code Playgroud)

插值是一个很棒的功能,请不要误会我的意思。但在我看来,它在那些缺少类似功能的语言中表现得最好string.Format,例如 JavaScript。

  • 我不同意可维护性;当然,ReSharper 可以更轻松地将插入的值与其相应的索引进行匹配(反之亦然),但我认为弄清楚“{3}”是 X 还是 Y 仍然需要更多的认知负担,尤其是当您开始重新排列格式时。Madlibs 示例:`$"It was a {adjective} day in {month} when I {didSomething}"` vs `string.Format("It was a {0} day in {1} when I {2}", adjective , Month, didSomething)` --&gt; `$"I {didSomething} on a {adjective} {month} day"` vs `string.Format("I {2} on a {0} {1} day", adjective ,月,做了某事)` (7认同)