使用字符串插值和使用字符串连接符之间有明显的性能差异吗?
myString += $"{x:x2}";
vs String.Format()?
与String.Format()相比呢?
myString += String.Format("{0:x2}", x);
我只是问一下,因为ReSharper正在提示修复,而我以前被愚弄过。
使用字符串插值和使用字符串连接符之间有明显的性能差异吗?
myString += $"{x:x2}";
vs String.Format()?
与String.Format()相比呢?
myString += String.Format("{0:x2}", x);
我只是问一下,因为ReSharper正在提示修复,而我以前被愚弄过。
可感知性是相对的。然而: 字符串插值在编译时被转换为string.Format()
,因此它们应该得到相同的结果。
不过,还有一些微妙的差别:我们可以从这个问题中看出,在格式说明符中的字符串连接会导致额外的string.Concat()
调用。
int
时),字符串插值可能会编译成字符串连接。var a = "hello"; var b = $"{a} world";
编译为字符串连接。
var a = "hello"; var b = $"{a} world {1}";
编译为字符串格式化。 - Omar MuscatelloReSharper
没有显示第三种变体,这也是最高效的一种。列出的两种变体会产生相同的 IL 代码,但下面的代码确实会提高性能。myString += $"{x.ToString("x2")}";
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>();
}
}
}
| 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 |
在 .NET 6.0.9.41905, X64 RyuJIT AVX2
上重新运行了测试。
| Method | Data | Mean | Gen0 | Allocated |
|-------------------- |----- |----------:|-------:|----------:|
| Format | 42 | 37.47 ns | 0.0089 | 56 B |
| Interpolate | 42 | 57.61 ns | 0.0050 | 32 B |
| InterpolateExplicit | 42 | 11.46 ns | 0.0051 | 32 B |
| Format | 1337 | 39.49 ns | 0.0089 | 56 B |
| Interpolate | 1337 | 59.98 ns | 0.0050 | 32 B |
| InterpolateExplicit | 1337 | 12.85 ns | 0.0051 | 32 B |
InterpolateExplicit()
方法更快,因为我们现在明确告诉编译器使用 string
。不需要对要格式化的 object 进行 boxing 。装箱的成本确实非常高昂。还请注意,NET 6
对所有方法都减少了 CPU 和内存分配。
在 .NET 7.0.122.56804, X64 RyuJIT AVX2
上重新运行了测试。
| Method | Data | Mean | Gen0 | Allocated |
|-------------------- |----- |----------:|-------:|----------:|
| Format | 42 | 41.04 ns | 0.0089 | 56 B |
| Interpolate | 42 | 65.82 ns | 0.0050 | 32 B |
| InterpolateExplicit | 42 | 12.19 ns | 0.0051 | 32 B |
| Format | 1337 | 41.02 ns | 0.0089 | 56 B |
| Interpolate | 1337 | 59.61 ns | 0.0050 | 32 B |
| InterpolateExplicit | 1337 | 13.28 ns | 0.0051 | 32 B |
.NET 6
以来没有重大变化。
x
为空将会崩溃。 - PangmyString += x.ToString("x2");
? - AlexandreSystem.Text.StringBuilder.Append(xxx)
或System.Text.StringBuilder.AppendFormat(xxx)
。+=
。请非常小心,仅针对小型字符串的小数量这样使用。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}";
String::Format
,有时则转换为String::Concat
。对于该页面上的性能测试,我认为其意义并不是特别明显:你传递给这些方法的参数数量是不确定的。Concat
并不总是最快的,而StringBuilder
也并不总是最慢的。 - Matthias Burgervar myErrorMessage = "Value must be less than {0:0.00} for field {1}".FormatWith(maximum, fieldName);
插值是一个很棒的功能,不要误解我的意思。但我认为,它在那些缺少类似于 string.Format
功能的语言中表现最好,例如 JavaScript。
$"It was a {adjective} day in {month} when I {didSomething}"
与 string.Format("It was a {0} day in {1} when I {2}", adjective, month, didSomething)
--> $"I {didSomething} on a {adjective} {month} day"
与 string.Format("I {2} on a {0} {1} day", adjective, month, didSomething)
。 - drzausstring.Format
,我认为您更不容易遇到此问题。但无论如何,这就是为什么我强调这是我的观点 :) - Zoltán Tamási+=
运算符。看起来你正在通过在循环中执行此操作来创建某个东西的十六进制输出。+=
),特别是在循环中,可能会导致难以发现的问题:分析转储时的OutOfMemoryException
将显示大量可用内存!在 Microsoft 网站上有关于 String.Format()
的重要说明:
https://learn.microsoft.com/en-us/dotnet/api/system.string.format?view=net-6.0#remarks
不必调用String.Format方法或使用复合格式字符串,如果您的语言支持,可以使用插值字符串。插值字符串是包含插值表达式的字符串。每个插值表达式都会根据表达式的值解析,并在字符串被赋值时包含在结果字符串中。