字符串输出:C#中的格式或concat?

时间:2008-08-19 15:46:54

标签: c# string coding-style string.format

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

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

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

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

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

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

我会选择第二个。

32 个答案:

答案 0 :(得分:156)

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

  

过早优化=失败。

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

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

答案 1 :(得分:88)

试试这段代码。

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

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 result = string.Format(“{0} {1}”,p.FirstName,p.LastName);采取:618ms - 2213706蜱   1000000 x result =(p.FirstName +“”+ p.LastName);花了:166ms - 595610滴答

答案 2 :(得分:55)

哦亲爱的 - 在阅读其中一条回复之后,我尝试颠倒操作的顺序 - 首先执行连接,然后执行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!”)会使所有时间重新排列。

答案 3 :(得分:36)

字符串是不可变的,这意味着在代码中反复使用相同的小块内存。将相同的两个字符串添加到一起并一遍又一遍地创建相同的新字符串不会影响内存。 .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

答案 4 :(得分:22)

可怜的翻译

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

所以请使用string.Format(),特别是如果您要将应用程序转到英语不是第一语言的任何地方。

答案 5 :(得分: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");

所以,我不知道回答标记作为答案:)

答案 6 :(得分:9)

在类似的简单场景中连接字符串很好 - 除了更复杂的东西之外,它更复杂,甚至是LastName,FirstName。使用这种格式,您可以一目了然地看到,在读取代码时字符串的最终结构是什么,通过连接,几乎不可能立即识别最终结果(除非有一个非常简单的例子)。 / p>

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

如果您使用的是.NET 3.5,则可以使用扩展方法like this one并轻松实现这样的袖口语法:

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);

答案 7 :(得分:7)

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

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

答案 8 :(得分: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,这也是我输出计时器滴答的原因。

答案 9 :(得分: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}";

答案 10 :(得分:5)

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

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

What is so bad about string concatenation?

答案 11 :(得分: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 + ".");

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

答案 12 :(得分:5)

一般来说,我更喜欢前者,特别是当字符串变长时,它可以更容易阅读。

另一个好处是我相信性能之一,因为后者在将最终字符串传递给Console.Write方法之前实际执行了2个字符串创建语句。 String.Format在我认为的封面下使用StringBuilder,因此避免了多个连接。

但是应该注意,如果传递给String.Format(以及其他类似方法,如Console.Write)的参数是值类型,那么它们将在传入之前被加框,这可以提供自己的性能命中。 Blog post on this here

答案 13 :(得分:5)

从C#6.0开始interpolated strings可用于执行此操作,从而进一步简化了格式。

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

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

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

请参阅this dotnetperls article字符串插值。

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

答案 14 :(得分:4)

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

如果要向用户显示,我会使用String.Format,以便我可以进行本地化,如果需要的话 - FxCop将为我拼写检查,以防万一:)

如果它包含数字或任何其他非字符串的东西(例如日期),我会使用String.Format,因为它给了我更多control over the formatting

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

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

如果它是用于某些输出的用户不会看到,并且不会影响性能我会使用String.Format,因为我习惯使用它而且我只是习惯了它:)< / p>

答案 15 :(得分:4)

  1. 格式化是“.NET”的方式。某些重构工具(Refactor!for one)甚至会建议重构concat风格的代码以使用格式化风格。
  2. 格式化更容易针对编译器进行优化(尽管第二种可能会被重构为使用快速的'Concat'方法。)
  3. 格式化通常更清晰(特别是“花式”格式化)。
  4. 格式化意味着对所有变量隐式调用'.ToString',这有利于提高可读性。
  5. 根据“Effective C#”,.NET的“WriteLine”和“Format”实现搞砸了,它们会自动放映所有值类型(这很糟糕)。 “有效的C#”建议明确执行'.ToString'调用,其中恕我直言是假的(见Jeff's posting
  6. 目前,编译器不会检查格式化类型提示,从而导致运行时错误。但是,这可以在将来的版本中进行修改。

答案 16 :(得分:3)

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

答案 17 :(得分:3)

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

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);

format选项让我读取变量名称并将它们映射到相应的数字。 concat选项不需要。我仍然对引号和+符号感到困惑,但另一种选择更糟糕。红宝石?

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

性能方面,我希望format选项比concat慢,因为format要求字符串解析。我不记得必须优化这种指令,但如果我这样做,我会查看stringConcat()Join()方法。

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

答案 18 :(得分:3)

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

  • 代码需要执行数百万次
  • 你要做大量的会用(超过4吨)
  • 代码针对Compact Framework

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

答案 19 :(得分:2)

由于我不认为这里的答案涵盖了所有内容,所以我想在这里做一些小补充。

Console.WriteLine(string format, params object[] pars)来电string.Format。 &#39; +&#39;意味着字符串连接。我不认为这总是与风格有关;我倾向于根据我所处的背景混合这两种风格。

简短回答

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

说你有

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堆栈上)。如果你在堆栈上推送一个字符串(例如IL中的ldstr表示文字),你就可以在堆栈上引用一个字符串指针。

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

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

所以,粗略的答案是:string.Format对于&gt; 1个concats,&#39; +&#39;将1个concat工作得很好。如果您不关心微观性能优化,string.Format在一般情况下也会正常工作。

关于文化的说明

然后有一种叫做文化的东西......

string.Format可让您在格式中使用CultureInfo。一个简单的操作员&#39; +&#39;使用当前的文化。

如果你正在编写文件格式和f.ex.这是一个特别重要的评论。 double您要添加的值{&};到一个字符串。在不同的计算机上,如果您不使用string.Format并使用明确的CultureInfo,则最终可能会使用不同的字符串。

F.ex。考虑一下如果你改变一个&#39;会发生什么。对于&#39;,&#39;在用荷兰语编写你的逗号分隔值文件时,小数点分隔符是一个逗号,所以你的用户可能会得到一个有趣的&#39;惊喜。

更详细的回答

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

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

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

StringBuilder是一个基本上以2的幂分配底层缓冲区的类。 string.Format使用了StringBuilder

这使得您的决定成为整体定位和追加(-multiple)(w / w.o。文化)或仅仅分配和追加之间的基本权衡。

答案 20 :(得分:2)

我认为这很大程度上取决于输出的复杂程度。我倾向于选择当时最适合的场景。

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

答案 21 :(得分: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

答案 22 :(得分:2)

我也更喜欢第二种,但我现在没有理性的论据来支持这种立场。

答案 23 :(得分:1)

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

答案 24 :(得分: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;
   }
}

结果:

Concat: 406 ticks
Concat: 356 ticks
Concat: 411 ticks
Concat: 299 ticks
Concat: 266 ticks
Format: 5269 ticks
Format: 954 ticks
Format: 1004 ticks
Format: 984 ticks
Format: 974 ticks
StringBuilder: 629 ticks
StringBuilder: 484 ticks
StringBuilder: 482 ticks
StringBuilder: 508 ticks
StringBuilder: 504 ticks

答案 25 :(得分:1)

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

答案 26 :(得分:1)

哦,只是为了完整性,以下是比正常连接更快的几个滴答:

Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));

答案 27 :(得分:1)

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

答案 28 :(得分:1)

我实际上喜欢第一个,因为当有很多变量与文本混合时,我觉得它更容易阅读。另外,使用string.Format(),呃,格式时更容易处理引号。这是字符串连接的decent analysis

答案 29 :(得分:1)

就个人而言,第二个就是你正在使用的所有内容都按照它将被输出的直接顺序。而第一个你需要将{0}和{1}与正确的var匹配,这很容易搞乱。

至少它没有C ++ sprintf那么糟糕,如果你得到变量类型错误,整个事情就会爆炸。

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

答案 30 :(得分:1)

最易读的是使用 C# import React, { useState } from "react"; import { EditorState, ContentState, convertToRaw } from "draft-js"; import { Editor } from "react-draft-wysiwyg"; import draftToHtml from "draftjs-to-html"; import htmlToDraft from "html-to-draftjs" import "./editor.css" import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css"; export const WYSIWYGEditor = (props) => { const maxLength = 10000; const blocksFromHtml = htmlToDraft(props.value); const { contentBlocks, entityMap } = blocksFromHtml; const [editorState, setEditorState] = useState(EditorState.createWithContent( ContentState.createFromBlockArray(contentBlocks, entityMap))); const onEditorStateChange = editorState => { setEditorState(editorState); return props.onChange( draftToHtml(convertToRaw(editorState.getCurrentContent())) ); }; return ( <React.Fragment> <div className="editor"> <Editor editorState={editorState} editorClassName="editorClassName" onEditorStateChange={onEditorStateChange} toolbar={{ options: ['inline', 'list'], inline: { options: ['bold', 'italic', 'underline', 'superscript', 'subscript'], }, list: { options: ['unordered', 'ordered'], }, }} handleBeforeInput={val => { const textLength = editorState.getCurrentContent().getPlainText().length; if (val && textLength >= maxLength) { return 'handled'; } return 'not-handled'; }} handlePastedText={val => { const textLength = editorState.getCurrentContent().getPlainText().length; return ((val.length + textLength) >= maxLength); }} /> </div> </React.Fragment> ); }; string interpolation 特性:

6.0

它的性能类似于使用“+”。

答案 31 :(得分:0)

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

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

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