今天,我在写C#代码时,突然发现一个最熟悉的陌生人 —— string.Format。在写C#代码的日子里,与它朝夕相伴,却没有真正去了解它。只知道在字符串比较多时,用它比用加号进行字符串连接效率更高(当然也更方便)。可是却从来没有问过为什么?

在生活中也有类似的现象,与你朝夕相处、你最熟悉的人,你往往不会进一步去了解她(他),你已经习惯了她(他),你认为你已经太了解她(他)了。。。真的是这样吗?这值得去思考。。。

博问中的一个问题 —— StringBuilder,String.concat(),String+String 哪一个效率高?激发了我的好奇心,想一探string.Format的究竟,而且在开发中也正好遇到一个字符串连接的问题。

了解.NET世界中的东西其实很简单,只要通过工具反编译出相应的.NET类库代码,我们来看看string.Fomat的代码:

  1. public static string Format(string format, object arg0, object arg1, object arg2)
  2. {
  3. if (format == null)
  4. throw new ArgumentNullException("format");
  5. return string.Format((IFormatProvider) null, format, arg0, arg1, arg2);
  6. }

实际调用的是另外一个签名的string.Format:

  1. public static string Format(IFormatProvider provider, string format, params object[] args)
  2. {
  3. if (format == null || args == null)
  4. throw new ArgumentNullException(format == null ? "format" : "args");
  5. StringBuilder stringBuilder = new StringBuilder(format.Length + args.Length * );
  6. stringBuilder.AppendFormat(provider, format, args);
  7. return ((object) stringBuilder).ToString();
  8. }

哦,原来用的就是StringBuilder(也许你早就知道了),string.Format只是StringBuilder的改装精简版。

既然是StringBuilder,它必然无法避免一个影响StringBuilder性能的问题 —— 初始化容量(capacity)的问题,string.Format是如何解决的呢?从上面的代码一眼就可以看出,初始化容量是这么计算出来的:

  1. format.Length + args.Length *

从这个计算公式可以看出,假设需要format的字符串是10个,如果这10字符串累加起来的字符数不超过80,就能发挥StringBuilder的最佳性能;否则,StringBuider需要扩容,从而带来性能损失。

所以,对于大字符串,string.Format不是最佳选择。

那最佳选择是什么?还是StringBuilder,只不过要自己写代码计算初始化容量。分享一下今天我们在实际开发中使用的代码:

  1. var bodyFormat = "<span id=\"comment_body_{0}\">{1}</span><br/>";
  2. var diggFormat = " <a href=\"javascript:void(0);\" onclick=\"voteComment({0},'Digg')\">支持({2})</a>";
  3. var buryFormat = " <a href=\"javascript:void(0);\" onclick=\"voteComment({0},'Bury')\">反对({3})</a>";
  4. var args = new string[]{ comment.ID.ToString(), comment.Body, comment.DiggCount.ToString(), comment.BuryCount.ToString() };
  5. //计算初始化容量
  6. int capacity = bodyFormat.Length + diggFormat.Length + buryFormat.Length;
  7. for (int i = ; i < args.Length; i++)
  8. {
  9. capacity += args[i].Length;
  10. }
  11. var sb = new StringBuilder(capacity);
  12. sb.AppendFormat(bodyFormat,args);
  13. sb.AppendFormat(diggFormat,args);
  14. sb.AppendFormat(buryFormat,args);
  15. Post.Text = sb.ToString();

这里没有使用string.Format,一是因为comment.Body的字符数会很多,string.Format分配的初始化容量不够。二是因为string.Format不能分批Fomat,格式字符串只能写在一起,造成格式字符串很长,也就是bodyFormat, diggFormat, buryFormat要拼成一个字符串。

麻烦主要在参数字符串(args)的长度计算,要将每个字符串的字符数进行累加。我们采用的方法是将所有参数放在string[]类型的变量中,通过遍历数组进行计算,然后将这个string[]类型的变量直接传给StringBuilder.AppendFormat(它支持的参数类型是object[])。

小结

写这篇博文不是为让你弃用string.Format,而是让你了解它所存在的限制,在某些性能要求极高的场景下,可以考虑到这个影响因素。

更新

针对这个问题,实现了两个扩展方法。

1. 针对单个格式字符串

  1. namespace System
  2. {
  3. public static class StringExtension
  4. {
  5. public static string FormatWith(this string format, params object[] args)
  6. {
  7. if (format == null || args == null)
  8. {
  9. throw new ArgumentNullException((format == null) ? "format" : "args");
  10. }
  11. else
  12. {
  13. var capacity = format.Length + args.Where(a => a != null).Select(p => p.ToString()).Sum(p => p.Length);
  14. Console.WriteLine(capacity);
  15. var stringBuilder = new StringBuilder(capacity);
  16. stringBuilder.AppendFormat(format, args);
  17. return stringBuilder.ToString();
  18. }
  19. }
  20. }
  21. }

调用示例:

  1. "welcome to {0}! welcome to {1}!".FormatWith("www.cnblogs.com", "q.cnblogs.com");

2. 针对多个格式字符串

  1. namespace System
  2. {
  3. public static class StringExtension
  4. {
  5. public static string FormatWith(this IEnumerable<string> formats, params object[] args)
  6. {
  7. if (formats == null || args == null)
  8. {
  9. throw new ArgumentNullException((formats == null) ? "formats" : "args");
  10. }
  11. else
  12. {
  13. var capacity = formats.Where(f => !string.IsNullOrEmpty(f)).Sum(f => f.Length) +
  14. args.Where(a => a != null).Select(p => p.ToString()).Sum(p => p.Length);
  15. var stringBuilder = new StringBuilder(capacity);
  16. foreach (var f in formats)
  17. {
  18. if (!string.IsNullOrEmpty(f))
  19. {
  20. stringBuilder.AppendFormat(f, args);
  21. }
  22. }
  23. return stringBuilder.ToString();
  24. }
  25. }
  26. }
  27. }

调用示例:

  1. new string[] { "welcome to {0}!", " welcome to {1}!" }.FormatWith("www.cnblogs.com", "q.cnblogs.com");

前面使用StringBuilder的代码改为调用扩展方法:

  1. Post.Text = new string[]{
  2. "<span id=\"comment_body_{0}\" class=\"blog_comment_body\">{1}</span><br/>",
  3. " <a href=\"javascript:void(0);\" class=\"comment_vote\" onclick=\"voteComment({0},'Digg')\">支持({2})</a>",
  4. " <a href=\"javascript:void(0);\" class=\"comment_vote\" onclick=\"voteComment({0},'Bury')\">反对({3})</a>"
  5. }.FormatWith(comment.ID, comment.Body, comment.DiggCount, comment.BuryCount);
  6.  
  7. 原文链接(http://www.cnblogs.com/dudu/archive/2012/05/29/string_format_stringbuilder.html

(转)使用string.Format需要注意的一个性能问题的更多相关文章

  1. C# String.Format的格式限定符与Format方法将多个对象格式化一个字符串原理

    Format方法将多个对象格式化成一个字符串Format方法解析格式字符串的原理: (1).格式字符串中的{0}会被替换成格式字符串之后的第一个参数,以此类推 (2).Format方法解析格式字符串时 ...

  2. c# 字符串连接使用“+”和string.format格式化两种方式

    参考文章:http://www.liangshunet.com/ca/201303/218815742.htm 字符串之间的连接常用的两种是:“+”连接.string.format格式化连接.Stri ...

  3. 【转】string.Format对C#字符串格式化

    转自:http://blog.csdn.net/samsone/article/details/7556781 1.格式化货币(跟系统的环境有关,中文系统默认格式化人民币,英文系统格式化美元) str ...

  4. C#中string.format用法详解

    C#中string.format用法详解 本文实例总结了C#中string.format用法.分享给大家供大家参考.具体分析如下: String.Format 方法的几种定义: String.Form ...

  5. string.Format格式化用法详解

    1.格式化货币(跟系统的环境有关,中文系统默认格式化人民币,英文系统格式化美元) string.Format("{0:C}",0.2) 结果为:¥0.20 (英文操作系统结果:$0 ...

  6. String.Format用法

    http://blog.csdn.net/yohop/article/details/2534907 1.作为参数   名称 说明   Format(String, Object) 将指定的 Stri ...

  7. JS字符串格式化函数 string.format

    原生JS写的仿C#的字符串format函数,在此基础上又增加了便于JS使用的字面量对象参数. 参照C#中的规则,调用的时候会检测字符串格式,如果字符串格式不规范,或者传入的参数为null或undefi ...

  8. 关于string.format() 转

    string.format()函数用来生成具有特定格式的字符串,这个函数有两个参数,第一个参数为格式化串:由指示符和控制格式的字符组成.第二个参数是对应格式中每个代号的各种数据. 格式字符串可能包含以 ...

  9. 【C#】 格式化说明符 string.Format WriteLine

    定义 格式说明符的语法由3个字段组成:索引号.对齐说明符和格式字段.String.Format和WriteLine都遵守同样的格式化规则. 对齐说明符 对齐说明符表示了字段中字符的最小宽度.对齐说明符 ...

随机推荐

  1. sqlserver的一些小知识点

    1.高效分页sql和储存过程 select top 每页条数 * from ( select ROW_NUMBER() over (order by id)as nid ,* from table01 ...

  2. WeMall微信商城源码插件会员卡代码详情

    WeMall微信商城源码插件会员卡代码是用于商业推广的比较有效的方式,分享了部分比较重要的代码,供技术员学习参考 Index_index.html <html> <head> ...

  3. MD5加密 32位

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; ...

  4. Oracle-orclEXORIM

    imp pzhdb/hiway@orcl file =d:\pzhsd.dmp fromuser = pzhsd touser=pzhdb:导入 第一个pzhdb为新的用户名 hiway为密码 orc ...

  5. Class.getResourceAsStream()与ClassLoader.getResourceAsStream()的区别

    Class.getResourceAsStream() 会指定要加载的资源路径与当前类所在包的路径一致. 例如你写了一个MyTest类在包com.test.mycode 下,那么MyTest.clas ...

  6. 解决MVC中JsonResult返回 弹出文件下载对话框

    设置一下返回类型为HTML TEXT就可以了 JsonResult json = Json(xxx, JsonRequestBehavior.DenyGet); json.ContentType = ...

  7. Dapper源码学习和源码修改

    之前ORM比较火热,自己也搞了个WangSql,但是感觉比较low,大家都说Dapper性能好,所以现在学习学习Dapper,下面简单从宏观层面讲讲我学习的Dapper. 再了解一个东西前,先得学会使 ...

  8. 【2017-03-24】CSS样式表

    CSS样式表:层叠式样式表 一.样式表的分类 1.内联式 写在标记的属性位置,优先级最高,重用性最差. 格式: <div style="width:100px;height:100px ...

  9. javascript中常用的

    1.javascript中构造equals().trim()方法并应用 String.prototype.Trim = function() { return this.replace(/(^\s*) ...

  10. Linux下快速搭建php开发环境

    php开发环境快速搭建 一.Linux下快速搭建php开发环境 1.安装XAMPP for Linux XAMPP(Apache+MySQL+PHP+PERL)是一个功能强大的建站集成软件包,使用XA ...