183

假设您要输出或连接字符串。你更喜欢以下哪种风格?

  • var p = new { FirstName = "Bill", LastName = "Gates" };

  • Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

  • Console.WriteLine(p.FirstName + " " + p.LastName);

您更愿意使用格式还是简单地连接字符串?什么是你最喜欢的?其中之一会伤害你的眼睛吗?

你有任何合理的论据来使用一个而不是另一个吗?

我会去第二个。

4

32 回答 32

161

我很惊讶有这么多人立即想要找到执行速度最快的代码。如果 100 万次迭代仍然需要不到一秒的时间来处理,那么最终用户是否会注意到这一点?不太可能。

过早的优化 = 失败。

我会选择这个String.Format选项,只是因为从架构的角度来看它最有意义。我不关心性能,直到它成为一个问题(如果确实如此,我会问自己:我需要一次连接一百万个名字吗?当然它们不会都适合屏幕......)

考虑如果您的客户以后想要更改它,以便他们可以配置是否显示"Firstname Lastname""Lastname, Firstname."使用“格式”选项,这很容易 - 只需换掉格式字符串。使用 concat,您将需要额外的代码。当然,在这个特定的例子中这听起来没什么大不了的,但可以推断。

于 2008-08-23T20:07:13.893 回答
89

试试这个代码。

这是您的代码稍作修改的版本。

  1. 我删除了 Console.WriteLine,因为它可能比我要测量的要慢几个数量级。
  2. 我在循环之前启动秒表并在之后立即停止它,这样如果函数需要例如 26.4 个滴答声来执行,我就不会失去精度。
  3. 您将结果除以一些迭代的方式是错误的。看看如果你有 1000 毫秒和 100 毫秒会发生什么。在这两种情况下,除以 1,000,000 后,您将得到 0 毫秒。

代码:

Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();


Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);

这些是我的结果:

1000000 x 结果 = string.Format("{0} {1}", p.FirstName, p.LastName); 花费:618ms - 2213706 滴答
1000000 x 结果 = (p.FirstName + " " + p.LastName); 耗时:166ms - 595610 滴答

于 2008-08-20T16:40:14.973 回答
54

哦,亲爱的-在阅读了其他答复之一后,我尝试颠倒操作的顺序-因此首先执行连接,然后执行 String.Format ...

Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks

所以操作的顺序有很大的不同,或者说第一个操作总是慢得多。

以下是多次完成操作的运行结果。我尝试过更改订单,但是一旦忽略第一个结果,事情通常遵循相同的规则:

Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks

如您所见,相同方法的后续运行(我将代码重构为 3 个方法)越来越快。最快的似乎是 Console.WriteLine(String.Concat(...)) 方法,然后是普通连接,然后是格式化操作。

启动的初始延迟可能是 Console Stream 的初始化,因为在第一次操作之前放置 Console.Writeline("Start!") 会使所有时间恢复正常。

于 2008-08-20T08:45:19.563 回答
37

字符串是不可变的,这意味着在您的代码中一遍又一遍地使用相同的小块内存。将相同的两个字符串加在一起并一遍又一遍地创建相同的新字符串不会影响内存。.Net 足够聪明,只需使用相同的内存引用。因此,您的代码并没有真正测试两种 concat 方法之间的区别。

试穿这个尺寸:

Stopwatch s = new Stopwatch();

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0;

Random random = new Random(DateTime.Now.Millisecond);

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (random.Next().ToString() + " " + random.Next().ToString());
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();

s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString());
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();

StringBuilder sb = new StringBuilder();
s.Start();
for(var i = 0; i < n; i++){
    sb.Clear();
    sb.Append(random.Next().ToString());
    sb.Append(" ");
    sb.Append(random.Next().ToString());
    result = sb.ToString();
}
s.Stop();
sbElapsedMilliseconds = s.ElapsedMilliseconds;
sbElapsedTicks = s.ElapsedTicks;
s.Reset();

Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks");
Console.WriteLine("****************");
Console.WriteLine("Press Enter to Quit");
Console.ReadLine();

样本输出:

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks
1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks
于 2012-11-13T01:13:15.097 回答
22

可怜可怜的译者

如果您知道您的应用程序将保持英语,那么很好,保存时钟滴答声。但是,许多文化通常会在例如地址中看到姓氏。

所以使用string.Format(),特别是如果你打算让你的应用程序去任何英语不是第一语言的地方。

于 2009-11-30T21:11:53.920 回答
14

这是我超过 100,000 次迭代的结果:

Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks

这是工作台代码:

Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

for (var i = 0; i < n; i++)
{
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();
    cElapsedMilliseconds += s.ElapsedMilliseconds;
    cElapsedTicks += s.ElapsedTicks;
    s.Reset();
    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    fElapsedMilliseconds += s.ElapsedMilliseconds;
    fElapsedTicks += s.ElapsedTicks;
    s.Reset();
}

Console.Clear();

Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");

所以,我不知道谁的回复标记为答案:)

于 2008-08-20T16:16:31.537 回答
9

在这样的简单场景中连接字符串很好 - 任何比这更复杂的东西都更复杂,即使是姓氏,名字也是如此。通过这种格式,您可以一眼看出在阅读代码时字符串的最终结构是什么,通过连接几乎不可能立即辨别出最终结果(除了像这样的一个非常简单的示例)。

从长远来看,这意味着当您回来更改字符串格式时,您要么能够弹出并对格式字符串进行一些调整,要么皱起眉头开始四处走动与文本混合的各种属性访问器,这更容易引入问题。

如果您使用的是 .NET 3.5,您可以使用类似这样的扩展方法,并获得一种简单的、即开即用的语法,如下所示:

string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);

最后,随着您的应用程序变得越来越复杂,您可能会决定在您的应用程序中合理地维护字符串,您希望将它们移动到资源文件中以进行本地化或简单地移动到静态帮助程序中。如果您一直使用格式,这将更容易实现,并且您的代码可以非常简单地重构为使用类似

string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);
于 2008-08-19T15:56:23.377 回答
8

从 C# 开始,可以使用6.0 插值字符串来执行此操作,这进一步简化了格式。

var name = "Bill";
var surname = "Gates";
MessageBox.Show($"Welcome to the show, {name} {surname}!");

内插字符串表达式看起来像包含表达式的模板字符串。内插字符串表达式通过将包含的表达式替换为表达式结果的 ToString 表示来创建字符串。

内插字符串与 String.Format 具有相似的性能,但由于值和表达式是内联插入的,因此提高了可读性并缩短了语法。

另请参阅有关字符串插值的dotnetperls 文章。

如果您正在寻找一种默认的方式来格式化您的字符串,这在可读性和性能方面是有意义的(除非微秒会对您的特定用例产生影响)。

于 2015-08-07T23:32:12.663 回答
7

对于非常简单的操作,我会使用连接,但是一旦超过 2 或 3 个元素,格式就变得更适合 IMO。

更喜欢 String.Format 的另一个原因是 .NET 字符串是不可变的,这样做会创建更少的临时/中间副本。

于 2008-08-19T15:50:44.170 回答
6

虽然我完全理解风格偏好并选择串联作为我的第一个答案,部分是基于我自己的偏好,但我的部分决定是基于串联会更快的想法。因此,出于好奇,我对其进行了测试,结果令人震惊,尤其是对于这么小的字符串。

使用以下代码:

    System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();

    var p = new { FirstName = "Bill", LastName = "Gates" };

    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");

    s.Reset();
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();

    Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");

我得到以下结果:

Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks

使用格式化方法慢了100多倍!!连接甚至没有注册为 1ms,这就是为什么我也输出计时器滴答声。

于 2008-08-19T16:02:27.967 回答
5

一般来说,我更喜欢前者,尤其是当字符串变长时,它会更容易阅读。

另一个好处是我相信其中一种表现,因为后者在将最终字符串传递给Console.Write方法之前实际上执行了 2 个字符串创建语句。String.Format我相信在幕后使用 StringBuilder,因此避免了多个连接。

但是应该注意的是,如果您传入的参数String.Format(以及其他类似的方法,如 Console.Write)是值类型,那么它们将在传入之前被装箱,这可能会影响其性能。博客文章在这里

于 2008-08-19T15:53:59.060 回答
5

对于基本的字符串连接,我通常使用第二种风格——更容易阅读和更简单。但是,如果我要进行更复杂的字符串组合,我通常会选择 String.Format。

String.Format 节省了大量的引号和优点......

Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp);
vs
Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + ".");

只保存了几个字符,但我认为,在这个例子中,格式使它更清晰。

于 2008-08-19T15:55:01.273 回答
5

更好的测试是使用 Perfmon 和 CLR 内存计数器来观察您的内存。我的理解是,您想要使用 String.Format 而不仅仅是连接字符串的全部原因是因为字符串是不可变的,您不必要地给垃圾收集器增加了临时字符串,这些临时字符串需要在下一次传递中回收。

StringBuilder 和 String.Format 虽然可能更慢,但内存效率更高。

字符串连接有什么不好?

于 2008-09-22T14:27:23.543 回答
5

从现在起一周后,即 2015 年 8 月 19 日,这个问题正好有七 (7) 年之久。现在有更好的方法来做到这一点。在可维护性方面更好,因为与仅连接字符串相比,我没有进行任何性能测试(但这些天重要吗?相差几毫秒?)。使用C# 6.0的新方法:

var p = new { FirstName = "Bill", LastName = "Gates" };
var fullname = $"{p.FirstName} {p.LastName}";

这个新特性更好,IMO,在我们的例子中实际上更好,因为我们有代码,我们在其中构建查询字符串,其值取决于某些因素。想象一个查询字符串,其中我们有 6 个参数。所以不要做一个,例如:

var qs = string.Format("q1={0}&q2={1}&q3={2}&q4={3}&q5={4}&q6={5}", 
    someVar, anotherVarWithLongName, var3, var4, var5, var6)

in 可以这样写,而且更容易阅读:

var qs=$"q1={someVar}&q2={anotherVarWithLongName}&q3={var3}&q4={var4}&q5={var5}&q6={var6}";
于 2015-08-12T03:18:07.657 回答
4
  1. 格式化是“.NET”的做法。某些重构工具(Refactor! for one)甚至会建议重构 concat 样式代码以使用格式化样式。
  2. 格式化更容易为编译器优化(尽管第二个可能会被重构为使用快速的“Concat”方法)。
  3. 格式通常更易于阅读(尤其是“花式”格式)。
  4. 格式化意味着对所有变量的“.ToString”隐式调用,这有利于可读性。
  5. 根据“Effective C#”,.NET 'WriteLine' 和 'Format' 实现被搞砸了,它们自动装箱所有值类型(这很糟糕)。“Effective C#”建议显式执行 '.ToString' 调用,恕我直言,这是虚假的(请参阅Jeff 的帖子
  6. 目前,编译器不检查格式化类型提示,导致运行时错误。但是,这可以在未来的版本中进行修改。
于 2008-08-19T15:59:21.623 回答
4

我会使用String.Format,但我也会在资源文件中包含格式字符串,以便可以将其本地化为其他语言。使用简单的字符串 concat 不允许您这样做。显然,如果您永远不需要本地化该字符串,那么这不是考虑它的理由。这实际上取决于字符串的用途。

如果要显示给用户,我会使用它,String.Format以便在需要时进行本地化 - FxCop会为我进行拼写检查,以防万一:)

如果它包含数字或任何其他非字符串内容(例如日期),我会使用String.Format它,因为它可以让我更好地控制格式

如果是为了构建像 SQL 这样的查询,我会使用Linq

如果要在循环内连接字符串,我会使用StringBuilder来避免性能问题。

如果它是用于用户看不到并且不会影响性能的某些输出,我会使用 String.Format 因为无论如何我都有使用它的习惯,而且我已经习惯了:)

于 2008-08-19T16:06:37.880 回答
4

我选择基于可读性。当变量周围有一些文本时,我更喜欢格式选项。在这个例子中:

Console.WriteLine("User {0} accessed {1} on {2}.", 
                   user.Name, fileName, timestamp);

即使没有变量名,您也能理解其含义,而 concat 中充斥着引号和 + 符号,让我感到困惑:

Console.WriteLine("User " + user.Name + " accessed " + fileName + 
                  " on " + timestamp + ".");

(我借了迈克的例子,因为我喜欢它)

如果格式字符串在没有变量名的情况下意义不大,我必须使用 concat:

Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

格式选项让我读取变量名称并将它们映射到相应的数字。concat 选项不需要。我仍然对引号和 + 符号感到困惑,但替代方案更糟。红宝石?

Console.WriteLine(p.FirstName + " " + p.LastName);

性能方面,我希望 format 选项比 concat 慢,因为 format 需要 string to be parsed。我不记得必须优化这种指令,但如果我这样做了,我会看看stringConcat()and之类的方法Join()

格式的另一个优点是格式字符串可以放在配置文件中。错误消息和 UI 文本非常方便。

于 2009-05-13T08:35:59.430 回答
3

如果您正在处理需要易于阅读的内容(这是大多数代码),我会坚持使用运算符重载版本,除非:

  • 代码需要执行数百万次
  • 你正在做大量的连接(超过 4 是一吨)
  • 该代码针对紧凑框架

在其中至少两种情况下,我会改用 StringBuilder。

于 2008-09-22T14:43:54.937 回答
3

如果您打算本地化结果,那么 String.Format 是必不可少的,因为不同的自然语言甚至可能没有相同顺序的数据。

于 2009-06-07T14:50:08.567 回答
2

我认为这在很大程度上取决于输出的复杂程度。我倾向于选择当时最有效的方案。

根据工作选择合适的工具 :D 看起来最干净的工具!

于 2008-08-19T15:50:34.933 回答
2

我也更喜欢第二种,但目前我没有合理的论据来支持这一立场。

于 2008-08-19T15:51:26.537 回答
2

好一个!

刚刚添加

        s.Start();
        for (var i = 0; i < n; i++)
            result = string.Concat(p.FirstName, " ", p.LastName);
        s.Stop();
        ceElapsedMilliseconds = s.ElapsedMilliseconds;
        ceElapsedTicks = s.ElapsedTicks;
        s.Reset();

而且它甚至更快(我猜 string.Concat 在两个示例中都被调用,但第一个需要某种翻译)。

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks
1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks
于 2008-08-20T18:07:42.207 回答
2

由于我认为这里的答案并不能涵盖所有内容,因此我想在这里做一点补充。

Console.WriteLine(string format, params object[] pars)来电string.Format。'+' 表示字符串连接。我不认为这总是与风格有关。我倾向于根据我所处的环境混合这两种风格。

简短的回答

您面临的决定与字符串分配有关。我会尽量让它简单。

说你有

string s = a + "foo" + b;

如果执行此操作,它将评估如下:

string tmp1 = a;
string tmp2 = "foo" 
string tmp3 = concat(tmp1, tmp2);
string tmp4 = b;
string s = concat(tmp3, tmp4);

tmp这里并不是一个真正的局部变量,但它是 JIT 的临时变量(它被推送到 IL 堆栈上)。如果您将字符串压入堆栈(例如ldstr在 IL 中用于文字),您会将对字符串指针的引用放在堆栈上。

您调用此引用的那一刻concat成为一个问题,因为没有任何包含两个字符串的可用字符串引用。这意味着 .NET 需要分配一个新的内存块,然后用两个字符串填充它。这是一个问题的原因是分配相对昂贵。

这将问题更改为:如何减少concat操作次数?

所以,粗略的答案是:string.Format对于 >1 个 concat,'+' 对于 1 个 concat 就可以了。而且,如果您不关心进行微性能优化,string.Format那么在一般情况下就可以正常工作。

关于文化的注释

还有一种叫做文化的东西...

string.Format使您可以CultureInfo在格式中使用。一个简单的运算符“+”使用当前文化。

如果您正在编写文件格式和 f.ex,这一点尤其重要。double您“添加”到字符串的值。在不同的机器上,如果您不使用string.Format显式的CultureInfo.

前任 考虑一下如果你改变一个 '.' 会发生什么。对于“,”,在编写逗号分隔值文件时......在荷兰语中,小数点分隔符是逗号,因此您的用户可能会得到一个“有趣”的惊喜。

更详细的回答

如果您事先不知道字符串的确切大小,最好使用这样的策略来过度分配您使用的缓冲区。松弛空间首先被填充,然后数据被复制进来。

增长意味着分配一个新的内存块并将旧数据复制到新缓冲区。然后可以释放旧的内存块。此时你得到了底线:增长是一项昂贵的操作。

最实用的方法是使用过度分配策略。最常见的策略是以 2 的幂来过度分配缓冲区。当然,您必须做得比这更聪明(因为如果您已经知道需要 128,那么从 1、2、4、8 增长是没有意义的字符),但你得到了图片。该策略确保您不需要我上面描述的太多昂贵的操作。

StringBuilder是一个基本上以 2 的幂为基础过度分配底层缓冲区的类。在引擎盖下string.Format使用。StringBuilder

这使您的决定成为过度分配和追加(-multiple)(w/wo 文化)或只是分配和追加之间的基本权衡。

于 2015-05-22T09:19:03.440 回答
1

就个人而言,第二个,因为您使用的所有内容都是直接输出的顺序。而第一个您必须将 {0} 和 {1} 与正确的 var 匹配,这很容易搞砸。

至少它不像 C++ sprint 那样糟糕,如果你把变量类型弄错了,整个事情就会崩溃。

此外,由于第二个是内联的,它不必对所有 {0} 事物进行任何搜索和替换,后者应该更快......虽然我不确定。

于 2008-08-19T15:49:57.010 回答
1

我实际上喜欢第一个,因为当有很多变量与文本混合时,对我来说似乎更容易阅读。另外,使用 string.Format() 时更容易处理引号,呃,格式。这是对字符串连接的体面分析。

于 2008-08-19T15:57:20.567 回答
1

我一直走 string.Format() 路线。能够将格式存储在像 Nathan 示例这样的变量中是一个很大的优势。在某些情况下,我可能会附加一个变量,但是一旦连接了多个变量,我会重构以使用格式。

于 2008-08-19T19:16:49.043 回答
1

哦,为了完整起见,以下是比正常连接快几个滴答声:

Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));
于 2008-08-20T08:38:11.343 回答
1

第一个(格式)对我来说看起来更好。它更具可读性,并且您不会创建额外的临时字符串对象。

于 2008-08-23T19:55:24.247 回答
1

我很好奇 StringBuilder 在这些测试中的立场。结果如下...

class Program {
   static void Main(string[] args) {

      var p = new { FirstName = "Bill", LastName = "Gates" };

      var tests = new[] {
         new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) },
         new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) },
         new { Name = "StringBuilder", Action = new Action(delegate() {
            StringBuilder sb = new StringBuilder();
            sb.Append(p.FirstName);
            sb.Append(" ");
            sb.Append(p.LastName);
            string x = sb.ToString();
         }) }
      };

      var Watch = new Stopwatch();
      foreach (var t in tests) {
         for (int i = 0; i < 5; i++) {
            Watch.Reset();
            long Elapsed = ElapsedTicks(t.Action, Watch, 10000);
            Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString()));
         }
      }
   }

   public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) {
      Watch.Start();
      for (int i = 0; i < Iterations; i++) {
         ActionDelg();
      }
      Watch.Stop();
      return Watch.ElapsedTicks / Iterations;
   }
}

结果:

连续:406 滴答声
连续:356 滴答声
连续:411 滴答声
连续:299 滴答声
连续:266 滴答声
格式:5269 刻度
格式:954 刻度
格式:1004 个刻度
格式:984 滴答声
格式:974 滴答声
StringBuilder:629 滴答声
StringBuilder:484 滴答声
StringBuilder:482 滴答声
StringBuilder:508 滴答声
StringBuilder:504 滴答声
于 2008-09-22T14:56:33.987 回答
1

根据 MCSD 准备材料,微软建议在处理非常少量的串联(可能是 2 到 4 个)时使用 + 运算符。我仍然不确定为什么,但这是需要考虑的事情。

于 2009-07-04T20:31:04.130 回答
1

最易读的是使用C#的字符串插值6.0功能:

Console.WriteLine($"{p.FirstName} {p.LastName}");

其性能类似于使用“+”。

于 2021-06-25T21:07:48.487 回答
0

实际上,我昨天进行了这些测试,但是已经很晚了,所以我没有回答。

归根结底,他们似乎平均花费了相同的时间。我做了超过 100000 次迭代的测试。

我也会尝试使用 StringBuilder,回家后我会发布代码和结果。

于 2008-08-20T13:43:31.030 回答